Introduction
Connectors are the means by which a user can interact with the Snapper they're owned by. They can be used to change configurations, add or remove Components, or provide a relationship between disparate parts of a larger system. Some usages such as stretching, reorienting, snapping, and connecting are so common that they have already been integrated into the system.
There are four archetypes of Connectors that are commonly used:
Manipulators
- Typically used to only allow the user to manipulate the Snapper. Stretching, reorienting, etc.
- Does not allow any connections.
- The base Manipulator class is not a usable general-purpose class. Instead, the Manipulator class should be extended before use.
ConnectPoints
- These are used when making single point connections. Think about attaching a leg to a table.
- Allows a single connection.
- The base ConnectPoint class is not usable as a general-purpose class. However TransformSnapPoint, a subclass of ConnectPoint, is usable for this purpose.
ConnectLines
- These are used when making connections along an entire line. These are often used by worksurface Snappers to allow pedestals to attach anywhere along the edge of a surface.
- Allows multiple connections.
- The base ConnectLine class is a usable general-purpose class.
ConnectFaces
- These represent a flat area with allowable connections along its entire surface. A wall panel may use a ConnectFace to allow free placement across the entire surface.
- Allows multiple connections.
- The base ConnectFace class is not usable as a general-purpose class.
Basic Usage & Interface
Construction
Connectors should be kept as fields on a Snapper; the Connectors maintain the connections to other Snappers, so they cannot be re-created on the fly. It has become somewhat common practice to create an initConnectors()
in a base class, and call it from the constructor. Then any sub-classes will override this method to create and initialize Connectors on that class. This is not a requirement just common practice. See code sample below for an example.
/** * Example Snapper with ConnectPoint */ public class ExampleConnectorSnapper extends ModelSnapper { /** * Dimensions. */ public double w = 0.5; public double d = 0.5; public double h = 0.5; /** * Connect lines. */ public ConnectLine frontLine; public ConnectLine rightLine; public ConnectLine backLine; public ConnectLine leftLine; /** * Connet points. */ public TransformSnapPoint heightSnap; public TransformSnapPoint monitorStandSnap; /** * Constructor. */ public constructor() { initConnectors(); } /** * Initialize connectors. */ extend public void initConnectors() { rightLine = ConnectLine(this, (w, 0, 0), (w, d, 0)); rightLine.setRule(ctAlways);
backLine = ConnectLine(this, (w, d, 0), (0, d, 0)); backLine.setRule(ctAlways);
leftLine = ConnectLine(this, (0, d, 0), (0, 0, 0)); leftLine.setRule(ctAlways);
frontLine = ConnectLine(this, (0, 0, 0), (w, 0, 0)); frontLine.setRule(ctAlways); heightSnap = TransformSnapPoint(this, (w/2, d/2, h), (0deg, 270deg, 0deg), ctNever);
monitorStandSnap = TransformSnapPoint(this, (w/2, d, h), (90deg, 0deg, 0deg), ctAlways); }
}
Append to ConnectorCollection
For the system to make use of Connectors on a Snapper, the connectors()
must be overridden. Whenever the system needs to know what Connectors a Snapper provides, it will call the connectors()
and pass it a ConnectorCollection
. Any Connectors currently available should be appended to this collection. Special care should be taken when removing Connectors that are currently connected from this collection as this behavior is undefined. An example connectors()
can be found below.
/** * Connectors. */ public void connectors(ConnectorCollection conColl) { super(..); conColl << rightLine << backLine << leftLine << frontLine; conColl << heightSnap << monitorStandSnap; }
Updating Connectors
As Snappers change size and configuration, their Connector's positions or orientations may need to be updated to reflect these changes.
Consider a worktop was initially placed at a size of 60". A Connector that allows width stretching will likely be placed 60" from the origin to appear on the right edge. If the same worktop is then stretched to 72", it no longer makes sense to keep the Connector at 60".
To address this, the updateConnectors()
should be overridden. In this method, the Connectors should be re-positioned and reoriented to reflect the Snapper's current configuration. In some cases, the system will know when it is appropriate to invoke this method. However, any time a change is made that is known to require an update to the Connectors, it is best to invoke the method explicitly. An example of updateConnectors()
can be seen below.
/** * Update connectors. */ public void updateConnectors() { super(..); frontLine.setPos((0, 0, 0)); frontLine.setEndPos((w, 0, 0));
rightLine.setPos((w, 0, 0)); rightLine.setEndPos((w, d, 0));
backLine.setPos((w, d, 0)); backLine.setEndPos((0, d, 0));
leftLine.setPos((0, d, 0)); leftLine.setEndPos((0, 0, 0));
heightSnap.setPos((w/2, d/2, h));
monitorStandSnap.setPos((w/2, d, h)); }
Connector Visible Position
In some cases, it may make sense to alter how Connectors appear. For example, it may be desirable to have a Connector placed at one location but appear at another. This would be because the actual location of a Connector affects how Snappers are aligned and connected, but the visible location of the Connector affects what the user sees. Work-surfaces often do this; they place their Connectors on the floor, but visually place them on the surface edges.
To adjust the visible location of a Connector, the visiblePosition()
should be overridden. The system will pass three arguments to this method: the Connector is asking the visible position of, the actual location of the Connector, and the Connector that the first Connector is connected to. The last argument may be null if the specified Connector is not connected. This method should return the desired location of the Connector. Alternatively, the visible location of the Connector can be adjusted for 2D or 3D views independently by overriding visible2DPosition()
and/or visible3DPosition()
. An example of visiblePosition()
can be seen below.
/** * Visible position. */ public point visiblePosition(Connector mySnap, point defaultPos, Connector attach) { if (mySnap == leftLine or mySnap == rightLine or mySnap == backLine or mySnap == frontLine) { return defaultPos + (0, 0, h); } if (mySnap == heightSnap) { return defaultPos + (0, 0, 8inch); } return super(..); }
Connector Visibility
The visible orientation of a Connector may be altered as well, though this is less common. To alter visible orientation, the visibleRotation()
should be overridden. The method signature is the same as visiblePosition()
, except for the second argument being the Connector's actual orientation. As with visiblePosition()
you also have the option of independently manipulating the visible rotation for 3D and 2D views via visible2DRotation()
and visible3DRotation()
.
It is also possible to make a Connector invisible to the user. When a Connector is invisible, it still behaves like any other Connector. However, the user will be unable to click on it to stretch it or disconnect it from another Connector. This is sometimes done for Connectors whose only purpose is to receive connections. To control whether or not a Connector is visible, the isVisible()
should be overridden. Similar to visible position and orientation the Connector's visibility can be manipulated for either 2D or 3D views independently using the isVisibleIn2D()
or isVisibleIn3D()
. An example of isVisible()
can be seen below.
/** * Is the connector visible? */ public bool isVisible(Connector mySnap) { if (mySnap == monitorStandSnap) { return false; } return super(..); }
Additional Functions
Alignment & Connection
Two of the most important functions of Connectors are aligning and connecting Snappers. While Connectors and Snappers handle alignment and connecting in similar ways, it is important to recognize that they are separate mechanisms, and take place at different times.
An Animation that repositions a Snapper, such as a DragAnimation
or InsertAnimation
, will not make any connections from the Snapper to other Snappers. Its purpose is to aid the user in either free-placing the Snapper, or aligning it in such a way that one of its Connectors is aligned with a Connector from the other Snapper. Once the Animation completes, the system will then look to see if there are any aligned Connectors, and then attempt to create the actual connection.
The alignment and connection processes share several steps, but also have some that are unique. This allows for Snappers that can align with each other without creating connections. It is also possible to connect Snappers without them being aligned. Knowing when to allow or disallow one or the other is entirely dependent on the context. Consider kitchen wall cabinets and countertops. Physically, there is no connection between the two. For this reason, it makes sense to disallow a connection between them in code. However, a user will typically want to align a cabinet so that its back is flush to the back of the countertop. For this reason, it does make sense to help the user to align the Snappers in code.
When working with Connectors, two terms will appear frequently snap and attach. All attempts to align or connect must first be permitted by both the Snapper attempting to make the connection and the one receiving the connection. The terms snap and attach refer to the Connector on the initiating Snapper and the Connector on the receiving Snapper, respectively. Code is commonly written to make choices based on what side of the event the Snapper is on. For example, some Snappers may permit being connected to another Snapper, but not the other way around. This happens most often when there is a real-world dependence between two Snappers. For example, it does not make much sense to attach a car to a set of tires, but attaching the tires to a car makes perfect sense.
Alignment
The alignment of Snappers and their Connectors is typically initiated via an Animation. However, before the Animation decides that two Snappers should be aligned, a series of steps are taken.
- The ConnectRules of the two applicable Connectors are compared to ensure that they are compatible.
animationTrySnap()
is invoked on the 'snap' Snapper to adjust it (if necessary) and potentially block the alignment.allowTrySnap()
andallowTryAttach()
are invoked to allow or disallow the alignment.allowSnap()
andallowAttach()
are invoked to allow or disallow the alignment.
This is only a simplified overview of the alignment process. Behind the scenes, there are other mechanisms at work that may be altered for additional customized behavior.
Connection
Connecting to Connectors is done from the Snappers connect method, which invokes Connector's connect method. Disconnecting calls the appropriately named disconnect method.
If there is any code you want to be triggered from connecting or disconnecting, there is also the connected and disconnected method which can be overridden to place your code in.
Comments
0 comments
Please sign in to leave a comment.