Introduction
Have you ever wondered how some snappers have selectable parts that bring up a relevant QuickProperty window when selected? How about animations that highlight, and act on a certain part of a snapper only? These are both possible in CET thanks to PickSurfaces. However, if your snappers are in G2, using Components would be a better implementation of such a feature.
Picksurfaces form links between Primitive3D objects and owners allowing for greater control over individual components of a snapper.
Cabinet Example
Example in base/custom/developer/umbrella/systems/nemesis/
Let's look at the cabinet snapper shown below:
This cabinet has three compartments, and they are not snappers on their own; they are just instances of the CabCompartment
class, which is a subclass of PropObj
. The CabCompartment
class stores information about the compartment (dimensions, 3D graphics etc). As you may have already guessed, these compartment instances (3 of them) are stored in a sequence in SimpleCabinet
.
In SimpleCabinet
's build3D()
, we run through the sequence of compartments, retrieve the compartment3D objects, and append them to the SimpleCabinet
's 3D graphics.
We now have the necessary structures to hold all compartment related information, and the complete 3D model that represents the SimpleCabinet snapper.
Linking Using Picksurfaces
We will now need to form links between the compartments and their 3D objects using picksurfaces. We need these links to be able to retrieve the compartment the user clicks on in the 3D view.
You can form the link by assigning an owner to the Primitive3D object belonging to the compartment as shown below.
cabCompartment.cm
/** * Get3D. */ public Primitive3D get3D(..) { ... compartment3D.setOwner(ExtendedPickSurface(cab, compartment3D, "CabCompartment", this)); ... }
Retrieving PickSurfaces on Click
We can then retrieve the selected picksurface via the StartClickAnimationEnv
object that is passed to clickAnimation()
in snapper.cm.
simpleCabinet.cm
/** * Click Animation */ public Animation clickAnimation(StartClickAnimationEnv env) { if (PickSurface ps = env.selection.pickSurface) pln(#ps); return super(env); }
Should You Store the Selected Object Somewhere?
It might be a good idea to store the selected object temporarily in the snapper, with stream=null
, of course, if there is a need to retrieve some information from it after the click event. Each time the user clicks on a compartment, we assign the compartment object to the selectedObject
field that we can retrieve through Picksurface
, as explained in the section above.
The selected object need not be stored if you only need to make some changes to it within the scope of the click event. Sometimes, you might only want to update some data contained within the selected object, so storing it will not give you any advantage.
simpleCabinet.cm
/** * Selected Obj */ private PropObj _selectedObj : copy=null, stream=null; extend public PropObj selectedObj=(PropObj n) { return _selectedObj = n; } extend public PropObj selectedObj() { return _selectedObj; }
/** * Click Animation */ public Animation clickAnimation(StartClickAnimationEnv env) { selectObj(env); return super(env); }
/** * Select obj */ extend public void selectObj(StartClickAnimationEnv env) { Object prevObj = selectedObj; selectedObj = null;
if (env and env.selection) { if (?ExtendedPickSurface ps = env.selection.pickSurface) { if (?PropObj prop = ps.v) selectedObj = prop; } } }
Adding Properties from the Selected Object
Let's now try to add a property to the cabinet compartment to show/hide the compartment handle. Here's what the final product should look like:
We want to be able to control the visibility of the handle for each compartment. We shall start by writing buildQuickProperties()
in the cabinet snapper class. We want to be able to retrieve properties from the selected object, which means, we will need to add a method to our compartment class to append the necessary properties.
simpleCabinet.cm
/** * Build quick properties */ public bool buildQuickProperties(QuickProperties props) { if (selectedObj as CabPart) selectedObj.appendQuickProperties(this, props); return true; }
cabCompartment.cm
/** * Append quick properties. */ public bool appendQuickProperties(SimpleCabinet cab, QuickProperties props) { props.append("showHandle", "Show handle", showHandle, BoolSubSet()); return super(..); }
It is important to keep in mind that buildQuickProperties()
is not called each time you click on a different part of a snapper. It is called when the snapper is selected for the first time. Therefore, we need to tell the system that the quick properties menu should be updated each time a compartment is selected.
Adding the last bolded line to selectObj()
would do the trick.
/** * Select obj */ extend public void selectObj(StartClickAnimationEnv env) { Object prevObj = selectedObj; selectedObj = null;
if (env and env.selection) { if (?ExtendedPickSurface ps = env.selection.pickSurface) { if (?PropObj prop = ps.v) selectedObj = prop; } } if (selectedObj) invalidateQuickProperties(); }
Handling Property Changes
Property changes can be handled as usual. You just have to pass the changes to the selected object's quickPropertyChanged()
.
simpleCabinet.cm
/** * Handle quick property changes. */ public bool quickPropertyChanged(QuickProperties props, str key, Object value, bool testChangeOnly) { if (selectedObj as CabCompartment) { selectedObj.quickPropertyChanged(this, props, key, value, testChangeOnly); } return true; }
cabCompartment.cm
/** * Handle quick property changes. */ extend public bool quickPropertyChanged(SimpleCabinet cab, QuickProperties props, str key, Object value, bool testChangeOnly) { if (testChangeOnly) return true; if (key == "showHandle") { if (value as Bool) { showHandle = value.v; cab.invalidate3D(); } } return true; }
End Result
Got everything to compile? Excellent! Here's what your cabinet should look like now:
Comments
0 comments
Please sign in to leave a comment.