Hey Juanjo!
It's never too late
Adjusted my code to your method as it will probably be more stable than mine ^.^
Thanks a lot for sharing your findings!
Take care,
Dziga
Found 17 posts.
Search results Show results as topic list.
Technical Discussion » HDA Python - create cam and frame selected geo issues
- dzigakaiser
- 17 posts
- Offline
Houdini Indie and Apprentice » Pyhton frame selected
- dzigakaiser
- 17 posts
- Offline
andehpandeh#get the render cam cam = hou.node('/obj/renderCAM') #get a sphere to focus on sphere = hou.node('/obj/sphere1/sphere1') #move the cam to focus on the bounding box of the sphere bBox = sphere.geometry().boundingBox() #set viewport to the render camera & frame the bounding box view = toolutils.sceneViewer().curViewport() view.setCamera(cam) view.lockCameraToView(True) view.frameBoundingBox(bBox)
Here's a little sample of accessing a cam and geometry, getting the bounding box of the geo and then framing the geo
in the viewport.
A problem that I've had is that everything works (the viewport switches to the render cam, locks the view) except .frameBoundingBox(), but if I run the code a second time, it will frame the camera to the bounding box of the geometry.
Any ideas on how to get the code to work by only running it once?
Hi andehpandeh,
the way I wrote it and it seems to work so far is this:
cam = obj_level.createNode('cam', 'asset_cam') node.setSelected(True,True) view.frameSelected() view.saveViewToCamera(cam) view.setCamera(cam)
The difference is that I create a new cam that frames the selected node.
If I already have a specific camera, I set it up manually and my tool uses it as is.
Possibly this will work for you as well to create a brand new cam. Or maybe you just need the saveViewToCamera() after framing, and then setting the cam in the viewport again.
So I would try this first in your case:
#get the render cam cam = hou.node('/obj/renderCAM') #get a sphere to focus on sphere = hou.node('/obj/sphere1/sphere1') #move the cam to focus on the bounding box of the sphere #bBox = sphere.geometry().boundingBox() #set viewport to the render camera & frame the bounding box view = toolutils.sceneViewer().curViewport() # view.setCamera(cam) view.lockCameraToView(True) sphere.setSelected(True, True) view.frameSelected() view.saveViewToCamera(cam) view.setCamera(cam)
You can of course also try this approach with the bbox instead ^.^
Let me know if this works! I am curious.
Good luck!
Dziga
Houdini Lounge » Is that possible to save .sim file when simulation is done?
- dzigakaiser
- 17 posts
- Offline
Hi Jisung,
I felt like trying to tackle your issue from another angle^.^ Attached you find a file with a Python based solution.
What it does:
1. When a frame is cooked, it adds the sim file name to a log file in the simulation directory.
2. It keeps the last X names in the log (you choose how many you want to keep in the Python SOP).
3. When a frame cooks, it deletes all other sim files except the last X amount.
What you have to do:
1. Connect the Python SOP to your dopnet, so it cooks each frame.
2. In the very top of the code section you see "USER INPUT".
2a. Add the path to your dopnet output node with which you save the sim files to disk.
2b. Enter the number of files you wish to keep (default = 3)
Known issue so far:
1. When you re-cache a sim that is still in memory, it seems to save the files to disk without cooking the Python code and thus not deleting files.
2. I would disable the Python SOP whenever you are not caching to disk. Otherwise it might get confused about which files to keep because of the way I wrote the script. Could be optimized ^^
The rest should work as is. In case you encounter errors, don't hesitate to contact me. I only tested on my Windows workstation with the simple file I uploaded.
There might be issues with other OS or file permissions, though it should work for Linux as well.
Let me know if this is what you wanted ^.^
Good luck!
Dziga
I felt like trying to tackle your issue from another angle^.^ Attached you find a file with a Python based solution.
What it does:
1. When a frame is cooked, it adds the sim file name to a log file in the simulation directory.
2. It keeps the last X names in the log (you choose how many you want to keep in the Python SOP).
3. When a frame cooks, it deletes all other sim files except the last X amount.
What you have to do:
1. Connect the Python SOP to your dopnet, so it cooks each frame.
2. In the very top of the code section you see "USER INPUT".
2a. Add the path to your dopnet output node with which you save the sim files to disk.
2b. Enter the number of files you wish to keep (default = 3)
Known issue so far:
1. When you re-cache a sim that is still in memory, it seems to save the files to disk without cooking the Python code and thus not deleting files.
2. I would disable the Python SOP whenever you are not caching to disk. Otherwise it might get confused about which files to keep because of the way I wrote the script. Could be optimized ^^
The rest should work as is. In case you encounter errors, don't hesitate to contact me. I only tested on my Windows workstation with the simple file I uploaded.
There might be issues with other OS or file permissions, though it should work for Linux as well.
Let me know if this is what you wanted ^.^
Good luck!
Dziga
Edited by dzigakaiser - July 14, 2021 16:21:22
Houdini Lounge » Diamond / Parallelogram Grid
- dzigakaiser
- 17 posts
- Offline
Hey cpb,
thanks for the file! Do you happen to know if that is still possible with the newer polybevel node?
I checked your file and the python shell shows me that your polybevel is just a "polybevel" but when creating a polybevel node myself it is a "polybevel::3.0" with completely different parameters.
I guess I could just make a small shelf tool to create the old bevel but would be curious how to accomplish this result with newer Houdini versions.
EDIT: I actually didn't figure out how to create the old bevel via Python. It always creates the new one :/
If anyone else knows it, feel free to share ^^
Take care,
Dziga
thanks for the file! Do you happen to know if that is still possible with the newer polybevel node?
I checked your file and the python shell shows me that your polybevel is just a "polybevel" but when creating a polybevel node myself it is a "polybevel::3.0" with completely different parameters.
I guess I could just make a small shelf tool to create the old bevel but would be curious how to accomplish this result with newer Houdini versions.
EDIT: I actually didn't figure out how to create the old bevel via Python. It always creates the new one :/
If anyone else knows it, feel free to share ^^
Take care,
Dziga
Edited by dzigakaiser - July 14, 2021 10:33:04
Technical Discussion » HDA Python - create cam and frame selected geo issues
- dzigakaiser
- 17 posts
- Offline
Update:
Using the orthographic views to frame and then save the view to a camera works and will be the workaround for now.
In case someone could still tell me if it is possible to create a cam, assign it to the viewport and then do the framing, that would be wonderful
Using the orthographic views to frame and then save the view to a camera works and will be the workaround for now.
In case someone could still tell me if it is possible to create a cam, assign it to the viewport and then do the framing, that would be wonderful
Technical Discussion » HDA Python - create cam and frame selected geo issues
- dzigakaiser
- 17 posts
- Offline
Hey guys,
I am currently working on an asset browser and its corresponding export from Houdini.
Currently, I am at the point where I would like to create, export and render multiple views from the HIP.
I am running into issues with the frameSelected() function in the Geometry Viewport class.
A) This works:
- get the viewport
- frame selected
- save view to camera
Result: camera with framed geo
Python extract:
B) This does not work:
- create a camera
- set camera to viewport
- lock camera to viewport
- frame selected
Result: camera as it was initially created
Running the frameSelected function from the shell after my external script, it correctly frames the selection.
Python extract:
C) This also does not work:
- create a camera
- set camera to viewport
- frame selected
- save view to camera
Result: camera with geometry that was framed inside the viewport before camera assignment
Python extract:
I use the first scenario to send the current view or camera to render. But I would like to also send custom top and side view cameras. For this I would like to make the other scenario work (creating a cam with specific initial rotations and then framing the geo with it).
Does anyone have any idea why it does not frame the selection from the created camera's view?
What I tried additionally:
- I explicitly select the geo in my script after camera creation to make sure the selection is correct.
- After cam creation and setting the viewport to the cam, I make sure the viewport is correct by getting it again with toolutils.sceneViewer().curViewport() before trying to frame the selection.
What I will try next:
- Using the existing orthographic top and side views to frame and then save the view to a camera. This should theoretically work as it does work with the perspective view in the first scenario above.
If anyone encountered anything like this or has any ideas, please let me know! I greatly appreciate any hints and help
Big thanks and take care,
Dziga
I am currently working on an asset browser and its corresponding export from Houdini.
Currently, I am at the point where I would like to create, export and render multiple views from the HIP.
I am running into issues with the frameSelected() function in the Geometry Viewport class.
A) This works:
- get the viewport
- frame selected
- save view to camera
Result: camera with framed geo
Python extract:
viewer = toolutils.sceneViewer() view = viewer.curViewport() cam = obj_level.createNode('cam', 'asset_cam') node.setSelected(True,True) view.frameSelected() view.saveViewToCamera(cam) view.setCamera(cam)
B) This does not work:
- create a camera
- set camera to viewport
- lock camera to viewport
- frame selected
Result: camera as it was initially created
Running the frameSelected function from the shell after my external script, it correctly frames the selection.
Python extract:
viewer = toolutils.sceneViewer() view = viewer.curViewport() cam_top = obj_level.createNode('cam', 'asset_cam_top') cam_top.parm('rx').set(-180) cam_top.parm('ry').set(-90) cam_top.parm('rz').set(90) view.setCamera(cam_top) view.lockCameraToView(1) node.setSelected(True,True) view.frameSelected()
C) This also does not work:
- create a camera
- set camera to viewport
- frame selected
- save view to camera
Result: camera with geometry that was framed inside the viewport before camera assignment
Python extract:
viewer = toolutils.sceneViewer() view = viewer.curViewport() cam_top = obj_level.createNode('cam', 'asset_cam_top') cam_top.parm('rx').set(-180) cam_top.parm('ry').set(-90) cam_top.parm('rz').set(90) view.setCamera(cam_top) node.setSelected(True,True) view.frameSelected() view.saveViewToCamera(cam_top)
I use the first scenario to send the current view or camera to render. But I would like to also send custom top and side view cameras. For this I would like to make the other scenario work (creating a cam with specific initial rotations and then framing the geo with it).
Does anyone have any idea why it does not frame the selection from the created camera's view?
What I tried additionally:
- I explicitly select the geo in my script after camera creation to make sure the selection is correct.
- After cam creation and setting the viewport to the cam, I make sure the viewport is correct by getting it again with toolutils.sceneViewer().curViewport() before trying to frame the selection.
What I will try next:
- Using the existing orthographic top and side views to frame and then save the view to a camera. This should theoretically work as it does work with the perspective view in the first scenario above.
If anyone encountered anything like this or has any ideas, please let me know! I greatly appreciate any hints and help
Big thanks and take care,
Dziga
Houdini Indie and Apprentice » Pyhton frame selected
- dzigakaiser
- 17 posts
- Offline
In theory it is this:
For me this works fine inside the Python shell or a Python SOP but I am having trouble using it in external Python scripts of my HDA. It somehow does not want to frame it from there:/
Maybe you will have more success there.
import toolutils viewer = toolutils.sceneViewer() view = viewer.curViewport() view.frameSelected()
For me this works fine inside the Python shell or a Python SOP but I am having trouble using it in external Python scripts of my HDA. It somehow does not want to frame it from there:/
Maybe you will have more success there.
Technical Discussion » H18 Circle union in forEach loop with feedback
- dzigakaiser
- 17 posts
- Offline
Hey Tomas,
wow, that's perfect! Thank you so much for helping out again!
I went down the path of looping because I couldn't figure out unifying them all at once but now I see where the problem lied.
Have a great weekend!
Dziga
wow, that's perfect! Thank you so much for helping out again!
I went down the path of looping because I couldn't figure out unifying them all at once but now I see where the problem lied.
Have a great weekend!
Dziga
Technical Discussion » H18 Circle union in forEach loop with feedback
- dzigakaiser
- 17 posts
- Offline
Hey there,
I have a forEach loop that copies circles to points and combines them with the output of the previous iteration.
Unfortunately, I am stuck right now to make this work properly with flat “2D” cirlces.
When going through the iterations step by step the boolean always combines the current circle with the last circle but not with the supposedly combined output from the last iteration.
I attached a file showing the issue. Simply go through the max iterations and you'll see how only the last two circles get combined.
Iteration 4:
Iteration 5:
The same setup seems to work fine when connecting a sphere which let's me assume that the setup should be correct like this, right?
It's the first time that I use the fetch feedback workflow inside the loop, so maybe I still made a mistake somewhere.
In the end, I would like to find a way to generate something like this but with combined shapes so I could potentially subtract multiple of those shapes from each other.
If someone has a different approach altogether, I am all ears.
Any help is greatly appreciated!
Edit: file reuploaded as it was the wrong one ^^
Take care and stay healthy!
Dziga
I have a forEach loop that copies circles to points and combines them with the output of the previous iteration.
Unfortunately, I am stuck right now to make this work properly with flat “2D” cirlces.
When going through the iterations step by step the boolean always combines the current circle with the last circle but not with the supposedly combined output from the last iteration.
I attached a file showing the issue. Simply go through the max iterations and you'll see how only the last two circles get combined.
Iteration 4:
Iteration 5:
The same setup seems to work fine when connecting a sphere which let's me assume that the setup should be correct like this, right?
It's the first time that I use the fetch feedback workflow inside the loop, so maybe I still made a mistake somewhere.
In the end, I would like to find a way to generate something like this but with combined shapes so I could potentially subtract multiple of those shapes from each other.
If someone has a different approach altogether, I am all ears.
Any help is greatly appreciated!
Edit: file reuploaded as it was the wrong one ^^
Take care and stay healthy!
Dziga
Edited by dzigakaiser - March 19, 2020 21:43:35
Technical Discussion » Houdini start up; start frame
- dzigakaiser
- 17 posts
- Offline
@jsmack:
Yeah, same ^.^
@Klonkel:
The easiest way would be to go to your Houdini user folder and create a “scripts” folder. In there you simply create a 123.py file with this content:
*Edited the code since I found a cleaner example from jsmack and xjorma here: Set range thread [www.sidefx.com]
If you use Houdini Core, the script needs to be called houdinicore.py.
Info about the start up scripts here:
Docs Python Script Locations [www.sidefx.com]
Take care,
Dziga
Yeah, same ^.^
@Klonkel:
The easiest way would be to go to your Houdini user folder and create a “scripts” folder. In there you simply create a 123.py file with this content:
#Houdini startup script import hou start = 1001 end = 1240 hou.playbar.setFrameRange(start, end) hou.playbar.setPlaybackRange(start, end) hou.setFrame(1001)
If you use Houdini Core, the script needs to be called houdinicore.py.
Info about the start up scripts here:
Docs Python Script Locations [www.sidefx.com]
Take care,
Dziga
Edited by dzigakaiser - Feb. 21, 2020 16:42:14
Technical Discussion » Houdini start up; start frame
- dzigakaiser
- 17 posts
- Offline
jsmackYes, I also think that is what OP meant^¬^ And I just thought that if you click “Save as default” in the global animation options and the future files do have the frame range set correctly, then they should maybe also have their current frame set to the start of this range and not to 1
Yeah, I thought you meant changing the start range for all future scenes.
Technical Discussion » Houdini start up; start frame
- dzigakaiser
- 17 posts
- Offline
I think he is just setting start and end in the global animation options and saving it as default. I just tried it and this indeed makes a new scene be set to 1001 - 1240 while having 1 as the current frame.
Startup script would be the way to go, though to be fair, it would be expected behaviour of Houdini to be on the first frame of the range on startup.
Houdini 18.0.348
Startup script would be the way to go, though to be fair, it would be expected behaviour of Houdini to be on the first frame of the range on startup.
Houdini 18.0.348
Edited by dzigakaiser - Feb. 21, 2020 11:06:44
Technical Discussion » Python - setting list parm on USD/Solaris instancer
- dzigakaiser
- 17 posts
- Offline
Okay, I was dumb ;D
The value token is “extsop” and not “External SOP”. Not sure, why I didn't think about checking the edit parameter interface panel before… ;P
The value token is “extsop” and not “External SOP”. Not sure, why I didn't think about checking the edit parameter interface panel before… ;P
Technical Discussion » python: Layout functions for nodes
- dzigakaiser
- 17 posts
- Offline
Hey Kym,
to your first question:
You can create a list with your created nodes and use the list in the layout function like so (of course you would append all your subnets and not just the one like in the example ):
Not sure what you mean. Do you mean you want to control if the layout is horizontal/vertical(see spacing options above)?
Or do you want to layout nodes which positions match a certain positional mask (e.g. nodes left of another node)?
Regarding your subnet question: if you look at your other thread I mentioned a different method to instance your assets with which you could just use a merge node in your subnet that merges all assets before passing them to the instancer.
If you want to keep your switch setup you could also put the switch into the subnet and use the one output of the subnet, right?
Edit: you find the layout functions here in the “Layout” section: hou.Node docs [www.sidefx.com]
Take care,
Dziga
to your first question:
You can create a list with your created nodes and use the list in the layout function like so (of course you would append all your subnets and not just the one like in the example ):
# node list for positioning later on subnets = [] # subnet = hou.node("/obj").createNode("subnet","subnet1") subnets.append(subnet) # layout of subnet nodes with optional spacing hou.node("/obj").layoutChildren(items=(subnets), horizontal_spacing=2.0, vertical_spacing=-1.0)
Is there a way to access the Layout nodes Up / left or right from python?
Not sure what you mean. Do you mean you want to control if the layout is horizontal/vertical(see spacing options above)?
Or do you want to layout nodes which positions match a certain positional mask (e.g. nodes left of another node)?
Regarding your subnet question: if you look at your other thread I mentioned a different method to instance your assets with which you could just use a merge node in your subnet that merges all assets before passing them to the instancer.
If you want to keep your switch setup you could also put the switch into the subnet and use the one output of the subnet, right?
Edit: you find the layout functions here in the “Layout” section: hou.Node docs [www.sidefx.com]
Take care,
Dziga
Edited by dzigakaiser - Feb. 19, 2020 06:55:05
Technical Discussion » transforming static alembic geometry from matrix
- dzigakaiser
- 17 posts
- Offline
Hey Kym,
I just did something quite similar (and also got awesome help from Tomas - thanks again ).
I also used the Python SOP method and put my final transform matrix into the “transform” point attribute.
One more way you can go about spreading your instances is using a CopyToPoints SOP, enabling pack and instance (optional) and using a piece attribute as an asset identifier.
You can set a piece attribute in your Python SOP when creating the point cloud and also add it to your loaded Alembics.
Then you merge all loaded assets into one stream and connect all points and all assets to one CopyToPoints SOP.
Take care,
Dziga
I just did something quite similar (and also got awesome help from Tomas - thanks again ).
I also used the Python SOP method and put my final transform matrix into the “transform” point attribute.
One more way you can go about spreading your instances is using a CopyToPoints SOP, enabling pack and instance (optional) and using a piece attribute as an asset identifier.
You can set a piece attribute in your Python SOP when creating the point cloud and also add it to your loaded Alembics.
Then you merge all loaded assets into one stream and connect all points and all assets to one CopyToPoints SOP.
Take care,
Dziga
Edited by dzigakaiser - Feb. 19, 2020 06:48:41
Technical Discussion » Python - setting list parm on USD/Solaris instancer
- dzigakaiser
- 17 posts
- Offline
Hey there!
I came across an issue and hope to find some help here once again I tried searching but didn't really know what to search for and ultimately ended up unsuccessful. I also posted over on odforce but so far yielded no replies.
I am trying to create a Solaris/USD network with my python script but when I set the list parameters on the instancer node it only changes the choice visually but does not actually use it.
As an example, when I set the “Prototype Index” to “Name Attribute” manually, the UI shows a new field to enter the name attribute. When I do this in Python:
the “Prototype Index” gets set to “Name Attribute” but the extra field is not shown and the node behaves as if it's set to its default value. If I then open the list and click on “Name Attribute” myself again, it shows the new line with my correct name attribute.
Additionally, I noticed now that sometimes the script does not even set the list parameter. E.g. when I open a fresh scene, use my script, it creates the instancer with default values (no errors though). When I run my script again, it creates a new instancer with the list set to my values, but still having the initial issue mentioned above :/
How would one go about solving this? Is there an extra function connected to this list choice that I have to trigger as well in Python?
As always, I am very grateful for any pointers, ideas and help!
Tested on MacOSX Mojave and CentOS 7 with Houdini 18.0.348. Small screenshot attached showing how I set the nodes parameter and it evaluates correctly in the shell but does not show in the interface and does not actually work.
Take care,
Dziga
I came across an issue and hope to find some help here once again I tried searching but didn't really know what to search for and ultimately ended up unsuccessful. I also posted over on odforce but so far yielded no replies.
I am trying to create a Solaris/USD network with my python script but when I set the list parameters on the instancer node it only changes the choice visually but does not actually use it.
As an example, when I set the “Prototype Index” to “Name Attribute” manually, the UI shows a new field to enter the name attribute. When I do this in Python:
usd_instancer.parm('protoindexsrc').set("Name Attribute") usd_instancer.parm('nameattr').set("instancename")
the “Prototype Index” gets set to “Name Attribute” but the extra field is not shown and the node behaves as if it's set to its default value. If I then open the list and click on “Name Attribute” myself again, it shows the new line with my correct name attribute.
Additionally, I noticed now that sometimes the script does not even set the list parameter. E.g. when I open a fresh scene, use my script, it creates the instancer with default values (no errors though). When I run my script again, it creates a new instancer with the list set to my values, but still having the initial issue mentioned above :/
How would one go about solving this? Is there an extra function connected to this list choice that I have to trigger as well in Python?
As always, I am very grateful for any pointers, ideas and help!
Tested on MacOSX Mojave and CentOS 7 with Houdini 18.0.348. Small screenshot attached showing how I set the nodes parameter and it evaluates correctly in the shell but does not show in the interface and does not actually work.
Take care,
Dziga
Houdini Indie and Apprentice » Houdini Indie + Engine, hbatch single frame still rendering watermark
- dzigakaiser
- 17 posts
- Offline
Hi dear community,
I am trying to render multiple mantra nodes via the Houdini Command Line Tools and hbatch with Houdini Indie + Indie Engine but I am running into problems. I searched the forums, google and odforce to find something on how to solve this without any luck. I am sure you'll be able to help me, though
Right now I have a hiplc file with 7 mantra outputs, they are all set to “Render current frame” since I need only one frame but with a resolution above the 4K animation limitation. The scene file itself is set to a range since I do have a matchmove camera.
In a .cmd file I have the following (from this thread https://www.sidefx.com/forum/topic/50702/ [www.sidefx.com]):
mread abc/abc.hiplc
render -F 1095 -V /out/modernHighriseB /out/modernHighriseD /out/modernHighriseF /out/modernHighriseH /out/modernHighriseI
The command line tells me it enters limited commercial licensing mode which is correct but it renders each output in 720p with watermark.
Yesterday I seemed to make it work by accidentally setting a frame outside my range which caused mantra to render frame 0000 without watermark. Unfortunately, that is not the frame I need.
So my question is: can I do something to render F1095 single frame above 4K via command line or does the limitation always happen once “frame != 0000”?
I usually have matchmove cameras for my shots, choose a good frame for the matte painting and render my elements for this frame. I know I could always create my still cam and then render frame 0000 but it is a workaround I would like to avoid and I hope I just missed something here you can help me with.
I am on Windows 7 in case that makes a difference ^^ If you need more information, please let me know. I am new here and more or less new to Houdini in general so maybe I am also doing something stupid here and you have alternative ways to go about it for me
Thank you in advance!
Take care,
Dziga
I am trying to render multiple mantra nodes via the Houdini Command Line Tools and hbatch with Houdini Indie + Indie Engine but I am running into problems. I searched the forums, google and odforce to find something on how to solve this without any luck. I am sure you'll be able to help me, though
Right now I have a hiplc file with 7 mantra outputs, they are all set to “Render current frame” since I need only one frame but with a resolution above the 4K animation limitation. The scene file itself is set to a range since I do have a matchmove camera.
In a .cmd file I have the following (from this thread https://www.sidefx.com/forum/topic/50702/ [www.sidefx.com]):
mread abc/abc.hiplc
render -F 1095 -V /out/modernHighriseB /out/modernHighriseD /out/modernHighriseF /out/modernHighriseH /out/modernHighriseI
The command line tells me it enters limited commercial licensing mode which is correct but it renders each output in 720p with watermark.
Yesterday I seemed to make it work by accidentally setting a frame outside my range which caused mantra to render frame 0000 without watermark. Unfortunately, that is not the frame I need.
So my question is: can I do something to render F1095 single frame above 4K via command line or does the limitation always happen once “frame != 0000”?
I usually have matchmove cameras for my shots, choose a good frame for the matte painting and render my elements for this frame. I know I could always create my still cam and then render frame 0000 but it is a workaround I would like to avoid and I hope I just missed something here you can help me with.
I am on Windows 7 in case that makes a difference ^^ If you need more information, please let me know. I am new here and more or less new to Houdini in general so maybe I am also doing something stupid here and you have alternative ways to go about it for me
Thank you in advance!
Take care,
Dziga
-
- Quick Links