Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
4 views14 pages

Connectors Hand Out

The document provides an overview of Connectors in CET Designer, detailing their role in user interaction with Snappers for space planning and configuration. It describes four archetypes of Connectors—Manipulators, ConnectPoints, ConnectLines, and ConnectFaces—along with their usage, interface, and methods for managing visibility and alignment. Additionally, it explains the alignment and connection processes between Snappers, emphasizing the importance of ConnectRules for filtering potential connections.

Uploaded by

rohithram5468
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views14 pages

Connectors Hand Out

The document provides an overview of Connectors in CET Designer, detailing their role in user interaction with Snappers for space planning and configuration. It describes four archetypes of Connectors—Manipulators, ConnectPoints, ConnectLines, and ConnectFaces—along with their usage, interface, and methods for managing visibility and alignment. Additionally, it explains the alignment and connection processes between Snappers, emphasizing the importance of ConnectRules for filtering potential connections.

Uploaded by

rohithram5468
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Connectors

All code samples in this document are taken from


extensions/custom/devTraining/connectors/

1 Introduction
CET Designer can be described as a “Space planning solution for highly configurable
products.” The two critical elements of this statement, “space planning” and “highly configurable”
are both made much easier for the user by means of Connectors. At the most fundamental level,
Connectors are simply a ’handle’ that provide some kind of user interaction with the Snapper they
are owned by. They can be used to change configurations, add or remove components, or provide a
’relationship’ between disparate parts of a larger system. However, certain usages of Connectors
are so commonplace that they have already been integrated into the system. Tasks such as
stretching, reorienting, snapping and connecting are already a part of most Connectors and
Snappers.
There are four ’archetypes’ of Connectors that are commonly used:
Manipulators
· These are typically used to just allow the user to manipulate the Snapper. Stretching,
reorienting, etc.
· Does not allow any connections.
· The base Manipulator class is not a useable general-purpose class. Instead, the
Manipulator class should be extended before use.
ConnectPoints
· These are used when making connections from a single point. Think about the posts on a
Lego® brick.
· Allows a single connection.
· The base ConnectPoint class is not useable as a general-purpose class. However,
TransformSnapPoint, a subclass of ConnectPoint, is useable 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 the
surface.
· Allows multiple connections.
· The base ConnectLine class is a useable general-purpose class.
ConnectFaces
· These represent a flat area with allowable connections along its entire surface. A wall or
panel may use a ConnectFace to allow free placement across their entire surface.
· Allows multiple connections.
· The base ConnectFace class is not useable as a general-purpose class.

2 Usage & Interface


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() method in a base class, and call it from the constructor.
Then, any subclasses will override this method to create and initialize Connectors on that class.
This is not any kind of requirement; it is just a common practice. See the code sample below for an
example:
1
/**
* Initialize Connectors.
*/
public void initConnectors() {
rightLine = ConnectLine(this, (w, 0, 0), (w, d, 0));
rightLine.setRule(dtDemoTableRule);
backLine = ConnectLine(this, (w, d, 0), (0, d, 0));
backLine.setRule(dtDemoTableRule);
leftLine = ConnectLine(this, (0, d, 0), (0, 0, 0));
leftLine.setRule(dtDemoTableRule);
frontLine = ConnectLine(this, (0, 0, 0), (w, 0, 0));
frontLine.setRule(dtDemoTableRule);
heightSnap = TransformSnapPoint(this, (w/2, d/2, surfaceH),
(0deg, 270deg, 0deg), ctNever);
monitorStandSnap = TransformSnapPoint(this, (w/2, d, surfaceH),
(90deg, 0deg, 0deg), dtDemoMonitorStandRule);
}
In order for the system to make use of Connectors on a Snapper, the connectors() method
must be overriden. Whenever the system needs to know what Connectors a Snapper provides, it
will call the connectors() method and passes it a ConnectorCollection. Any Connectors
currently available should be appended to this collection. This code can be somewhat dynamic. For
example, if certain Connectors should only be usable in specific conditions, this method may
append those Connectors conditionally. However, care should be taken if those Connectors are
connected to anything; behavior is undefined if a connected Connector ceases to be appended to
this collection. An example of a potential connectors() method may be seen in the code sample
below:
/**
* Connectors.
*/
public void connectors(ConnectorCollection connColl) {
super(..);
connColl << rightLine << backLine << leftLine << frontLine;
connColl << heightSnap << monitorStandSnap;
}
As Snappers change size and configuration, their Connectors’ positions or orientations may
need to be updated to reflect these changes. Consider a worktop that 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 out to a size of 72”, it no longer makes
sense to keep the Connector at 60”. To address this, the updateConnectors() method should be
overriden. In this method, the Connectors should be repositioned 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 in the code sample below:

2
/**
* Update Connectors.
*/
public void updateConnectors() {
super();
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, surfaceH) );
monitorStandSnap.setPos( (w/2, d, surfaceH) );
}
In certain situations, it makes 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 due to
the fact that the actual location of a Connector has an effect on how Snappers are aligned and
connected to each other, but the visible location of the Connector affects what the user sees.
Worksurfaces 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() method should be
overridden. The system will pass three arguments to this method: the Connector it 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 by null if the specified Connector is not connected to
anything. This method should return the desired visible location of the Connector. Alternatively, the
visible location of the Connector can be adjusted for 2D or 3D views independantly. To accomplish
this, the visible2DPosition() and visible3DPosition() methods may be overriden (these
methods take the same arguments). An example of visiblePosition() can be seen in the code
sample below:
/**
* Connector 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, surfaceH);
if (mySnap == heightSnap)
return defaultPos + (0, 0, 8inch);
return super(..);
}
The visible orientation of a Connector may be altered as well, but this is less common. To
alter the shown orientation, the visibleRotation() method should be overriden. The method
signature is similar to that of visiblePosition(), with the exception that it passes the Connector
’s actual orientation as the second argument. As is the case with visiblePosition(),
visibleRotation() also has 2D and 3D exclusive varients: 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 recieve connections. To control whether or not a Connector is visible, the isVisible()
method should be overridden. The only argument to this method will be the Connector that the

3
system will query for visibility. Again, isVisible() has 2D and 3D varients: isVisibleIn2D() and
isVisibleIn3D(). An example of isVisible() can be seen in the code sample below:
/**
* Is the connector visible?
*/
public bool isVisible(Connector mySnap) {
if (mySnap == monitorStandSnap)
return false;
return super(..);
}

3 Alignment & Connection


Two of the most important functions of Connectors are aligning and connecting Snappers.
While Connectors and Snappers handle alignment and connection 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 DragAnimation or InsertAnimation, will
not make any connections from that 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 on another 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 process and the connection process share several steps, but also have some
steps that are unique to each. This allows for Snappers that can align to each other, but without
creating connections. It is also possible to connect Snappers that have not been aligned, but that is
beyond the scope of this document. Knowing when to allow or disallow one or the other is entirely
dependant on the context. Consider kitchen wall cabinets and kitchen 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 makes sense to help the user 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, as well as the Snapper that will be receiveing 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 dependance
between two Snappers. For example, it does not make much sense to attach a car to a set of tires,
but attaching tires to a car
makes perfect sense.
The terms ’snap’ and ’attach’ will occur frequently, both in code and in this document. The
Snappers that own these Connectors will be referred to as the ’snap’ Snapper and the ’attach’
Snapper.

4 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:
1. The ConnectRules of the two applicable Connectors are compared to ensure that they are
compatible.
2. animationTrySnap() is invoked on the ’snap’ Snapper to adjust it (if neccessary) and
potentially block the alignment.

4
3. allowTrySnap() and allowTryAttach() are invoked to allow or disallow the alignment.
4. allowSnap() and allowAttach() 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. However, they
are beyond the scope of this document. The steps above are described in detail in the sections that
follow.

4.1 Connect Rules


When programming specifications or rules that control whether or not two Connectors
should align or connect to each other, complex calculations may be required. Product specifications
may disallow products to align/connect to each other based on sizes, configuration, or the status of
a larger system. Also, consider that without any kind of filtering, a user moving a Snapper around a
drawing is (without knowing) attempting to align every Connector on that Snapper to every other
Connector in the drawing. This could mean heavy calculations being run on a large number of
possible alignments/connections, every time the user moves the Snapper.
To help mitigate the problem, Connectors make use of ConnectRules. The ConnectRules
are used as a fast means of filtering out alignments/connections that will never be possible. This
limits the more complex calculations to only run on alignments/connections that might be possible.
A ConnectRule is defined by ConnectTypes. As the name implies, a ConnectType is a type of
connection, or a context for a connection. For example, a worksurface to worksurface connection
could be described by a ConnectType. A car wheel connecting to a car axle could be described by a
ConnectType. A ConnectRule is defined by choosing ConnectTypes to permit. For two Connectors
to align or connect, their ConnectRules must both allow a common ConnectType.
Knowing the various subclasses of ConnectRule is typically unnecessary as the helper
functions described later will automatically create ConnectRules of the appropriate class. However,
it is important to distinguish between symmetric and asymmetric ConnectRules. Symmetric
ConnectRules specify what ConnectTypes they permit in any context, while asymmetric
ConnectRules may specify ConnectTypes they permit in either a ’snap’ or ’attach’ context. For
example, it is possible to specify that a rule should allow worksurface to panel connections when
snapping, but allow worksurface to worksurace and worksurface to support connections when
being attached to. It is also possible to specify null for either the ’snap’ or ’attach’ ConnectTypes,
indicating that alignment/connection is not allowed in that context.
The easiest way to create ConnectRules is by calling one of the makeConnectRule()
functions. All variations take a str key as a first argument. Currently, this argument is not very
important, so it is typically the same as the name of the global variable that will contain the resulting
rule. Following the key, the arguments used depend on the desired rule:
Symmetric
Pass a single ConnectType. See example 1 in the code sample below
1:1 Asymmetric
Pass a single ConnectType for snapping and a single ConnectType for attaching. Either may be
null to prevent alignment/connection in that context. See example 2 in the code sample below
Many:Many Asymmetric
Pass a set of ConnectTypes for snapping and a set of ConnectTypes for attaching. Either may
be null to prevent alignment/connection in that context. See example 3 in the code sample
below
The makeConnectRule() functions all return a ConnectRule. This ConnectRule should be
stored in a global scope so it can be used by Connectors throughout the extension that defines
them and any other dependant extensions. The code sample below is an example of a file
dedicated to creating ConnectRules. Setting aside a file specifically for ConnectRules is common
practice. Note that the actual initialization of the ConnectRules takes place inside of an init block.

5
This is not neccessary, but is often practical; putting the code in an init block ensures the rules
will be available when referenced, but saves start-up time.
public class DtDemoTableEdgeCType extends ConnectType { }
public class DtDemoTableAccessoryCType extends ConnectType { }
public class DtDemoTableMonitorStandCType extends ConnectType { }

public ConnectRule dtDemoTableRule;


public ConnectRule dtDemoTableAccessoryRule;
public ConnectRule dtDemoMonitorStandRule;

init {
// Example 1: Symmetric
dtDemoMonitorStandRule = makeConnectRule("dtDemoMonitorStandRule",
DtDemoTableMonitorStandCType);

// Example 2: 1:1 Asymmetric


dtDemoTableAccessoryRule = makeConnectRule("dtDemoTableAccessoryRule",
DtDemoTableAccessoryCType, null);

// Example 3: Many:Many Asymmetric


dtDemoTableRule = makeConnectRule("dtDemoTableRule",
{ConnectTypeClass: DtDemoTableEdgeCType},
{ConnectTypeClass: DtDemoTableEdgeCType,
DtDemoTableAccessoryCType});
}

4.2 Snapper methods


After the checks against ConnectRules passes, the system then goes on to a handful of
methods on the Snappers involved in the alignment. These methods all have default
implementations that allow for new connections to be made; not all of the methods need to be
overridden. In fact, it is uncommon to override more than one or two.
The first method to be invoked is animationTrySnap(). This method is invoked on the ’snap’
Snapper, and is passed the two Connectors for the potential alignment. The first Connector will
always be owned by the ’snap’ Snapper, and the second Connector will be owned by the ’attach’
Snapper. Note that a reference to a Connector’s owner may always be retrieved from the snapper
field on the Connector. ex. attach.snapper is the owning Snapper of the attach Connector.
animationTrySnap() has two potential uses when overridden: it can allow or block the
alignment, and it can make reactionary changes to the ’snap’ Snapper. To control the potential
alignment, this method should return false to dissallow the alignment, or return the results from a
call to super(..) to allow the alignment. The call to super(..) is especially important here, as it
will perform additional actions, such as repositioning the ’snap’ Snapper such that the Connectors
are correctly aligned.
animationTrySnap() is also the appropriate place to make changes to the ’snap’ Snapper in
anticipation of being connected to the ’attach’ Snapper. For example, a worksurface may change its
dimensions to match a panel. Or, if a product requires special bracketry to attach to another
product, they can be added at this time. It is important to note that the call to super(..) will
attempt the actual alignment. So, anything that might affect the alignment should happen before
calling super(..). However, sometimes is desirable to hold off making any changes until it is
confirmed that the alignment can take place. For this, a call to super(..) should be made first, and
if that call returns a value of true, changes can be made. In the code sample below, a Snapper is
using animationTrySnap() to update its placement height to match the height of another Snapper.

6
/**
* Animation try snap.
*/
public bool animationTrySnap(Connector mySnap, Connector attach,
line mouseLine, bool connect=true, bool undoable=false,
ModifyEnv env=null) {
if (?DtDemoTable table = attach.snapper) {
surfaceH = table.surfaceH;
invalidate();
}
return super(..);
}
During animationTrySnap() (in the call to super(..)), the system makes calls to
allowTrySnap() and allowTryAttach() on the ’snap’ Snapper and the ’attach’ Snapper,
respectively. This is why the call to super(..) is important; without it the system cannot continue
with the alignment. As with animationTrySnap(), these methods may be used to control
alignment. They each will recieve the applicable Connectors as arguments. Here, a call to
super(..) is not required if the Snapper is actively deciding to allow or disallow the alignment.
Returning values of true or false will allow or disallow the alignment.
After allowTrySnap() and allowTryAttach(), the system will then call allowSnap() and
allowAttach(). For the most part, these methods are identical in function to the methods in the
previous step. The difference is that these methods are used in both the alignment and the
connection phases; the previously mentioned methods are only involved in the alignment phase.
If all of these checks pass, the two Connectors and their Snappers will be placed in such a
way that they will be sufficiently aligned for a connection.
Default behavior for Connectors is to only consider themselves aligned to each other when
their positions are flush. However, in some situations it is desirable to have Connectors snap and
connect at an offset. For example, worksurfaces may mount at an offset on their back edge to
allow for cables to run behind them. The obvious answer would be to place the Connector itself at
a slight offset. The only problem with this is that it will cause all connections to that Connector to
be at an offset.
As an alternative, the connectOffset() method may be overriden. This method, when
overriden, may specify an offset to snap / connect at. The method is passed four arguments: the
Connector that the system is querying an offset for, the Connector that the system is attempting to
snap / connect to, and two flags snap and attach. Note that the last two arguments are no longer
implemented, and their values should be ignored. Since the method recieves the ’attach’ Connector,
this method may return situation specific offsets. For example, a worksurface may connect at an
offset from a wall, but flush to another worksurface. Note that the returned offset should be an
offset from the position of the Connector (this could be described as Connector local coordinates).
An example of connectOffset() may be seen in the code sample below.
/**
* Connection offset.
*/
public point connectOffset(Connector mySnap, Connector attach,
bool snapping=false, bool attaching=true) {
if (mySnap == backLine)
return (.5inch, 0, 0);
return super(..);
}

5 Connecting
As stated before, placement Animations will not actually connect two Snappers until the
user has completed the placement/alignment. After the Animation has completed, the system will

7
then check all of the Connectors on the placed Snapper to determine if they should be connected
to nearby Connectors. For any two pairs of Connectors, the following steps are taken to potentially
connect them:
1. Invoke connectableTo() on both Connectors. This method will check that Connectors:
o Are sufficiently aligned, based on position and orientation
o Have compatible ConnectRules
o Meet other miscellaneous criteria, often specific to the specific type of Connectors
involved
2. Invoke allowSnap() and allowAttach() on the owners of the Connectors
3. Invoke connect() on the Connectors to complete the connection
connectableTo() is overriden to behave slightly different for each of the various subclasses
of Connector. However, the steps it takes are typically similar. All Connectors will make
ConnectRule comparisons in this call. This check is carried out the same way as it is done during
the alignment phase; both ConnectRules are checked to see if they are compatible with the other.
All Connectors will check to be sure they are sufficiently aligned before the connection is
allowed. Connectors must have opposite orientations to be aligned. For example, a Connector that
is oriented down the positive X-axis of the drawing may only be paired with a Connector oriented
down the negative X-axis. The image below shows two pairs of Connectors such that the
Connectors with the green arrows are correctly aligned to allow a connection, and the Connectors
with the red arrows are misaligned. Connectors must also be aligned by their positions, but this will
mean different things to each archetype: ConnectPoints only require their positions to be aligned,
but ConnectLines accept positions anywhere along the line that they represent.

Once the Connectors are verified to be sufficiently aligned, the system will then call
allowSnap() and allowAttach() on the ’snap’ Snapper and the ’attach’ Snapper. Note that this is
the same method that is called during the alignment phase. For this reason, these methods serve
as control over both snapping and connecting; returning false from these methods will deny both.
Finally, the connect() methods are invoked on the ’snap’ and the ’attach’ to create the actual
connection. In sub-classes of Connectors it is possible to override these methods to make
additional, final checks before allowing the connection. This is, however, unusual; it is often easier
to inject checks and behavior at other stages of the process.
There are two methods that are invoked on Snappers when connections are made or broken:
connected() and disconnected(). These methods will recieve, as arguments, the two Connectors
involved in the event. These methods allow the Snapper to react to changes in its connections.

8
When overriding connected() and disconnected() there are two things to consider. These
methods cannot influence the connection or disconnection; they can only react to the change. To
influence the connection, steps from the previous sections should be used. There is no easy way to
influence a disconnection. Additionally, both Snappers involved in the event will recieve the
methods called, but the order that they are called is somewhat arbitrary, and no assumptions should
be made about the order they are called.

6 Stretching
Connectors are also used for stretching Snappers. Stretching is commonly used to resize
symbols, but can also be used in most situations where a click-and-drag interaction is desired. For
example, stretching could be used to adjust the elevation of wall-mounted storage.
Before a Connector may be used for stretching, the system must know that it is allowed to
do so. This is accomplished by overriding the stretchable() method. The system will pass to this
method a Connector that it wants to query as being stretchable. Returning values of true or false
will allow or dissalow that Connector to be stretched. Note that a Connector that is used for
creating connections may also be used for stretching. An example of the stretchable() method
can be seen in the code sample below.
/**
* Is the connector stretchable?
*/
public bool stretchable(Connector mySnap) {
if (mySnap == leftLine or
mySnap == rightLine or
mySnap == frontLine or
mySnap == backLine or
mySnap == heightSnap)
return true;
if (mySnap == monitorStandSnap)
return false;
return super(..);
}
With the default implementation, a stretch is carried out by a LineStretchAnimation. Note
that a different Animation may be used by overriding the stretchAnimation() method of Snapper
, however, the rest of this section assumes the default behavior.
The Animation will begin with calling the beginStretch() method on the Snapper that owns
the Connector being stretched. This is an ideal opportunity to append animationProperties. The
details of working with animationProperties are beyond the scope of this document, however,
examples of their usage can be seen in the next two code samples. animationProperties should
be used for almost all stretches; users have an expectation for that extra feedback and control to
be there.

9
/**
* Begin stretch.
*/
public void beginStretch(Connector mySnap) {
super(..);
if (mySnap == leftLine or mySnap == rightLine) {
animation.createAnimationProperties();
animationProperties.append3("w", $width, w.distance,
widthDomain.toDistanceSubSet());
animationProperties.finalize(["w"]);
} else if (mySnap == frontLine or mySnap == backLine) {
animation.createAnimationProperties();
animationProperties.append3("d", $depth, d.distance,
depthDomain.toDistanceSubSet());
animationProperties.finalize(["d"]);
} else if (mySnap == heightSnap) {
animation.createAnimationProperties();
animationProperties.append3("h", $depth, surfaceH.distance,
surfaceHeightDomain.toDistanceSubSet());
animationProperties.finalize(["h"]);
}
}
While the user is dragging their cursor around, the Animation will invoke the stretch()
method on the Snapper. This method will be passed three arguments: the Connector involved in
the stretch, the position of the cursor (in Snapper local coordinates), and an AnimationMouseInfo
that contains additional information about the state of the Animation. Note that the position of the
cursor will be bound to a line that is projected in the direction that the Connector is oriented. For
example, if the Connector is facing down the X-axis, the point being passed to stretch() will
always be a point on that axis that is closest to the user’s cursor.
The stretch() method should be overriden to react to the user’s input during the stretch.
The common case is to change dimensions or reposition the Snapper in this method, but there are
not many limitations on what else can be done. After any changes are made, this method should
return true or false to reflect whether or not a change was made. An example of the stretch()
method may be seen in the code sample below.

10
/**
* Stretch.
*/
public bool stretch(Connector mySnap, point mousePos,
AnimationMouseInfo mouseInfo) {
if (mySnap == leftLine or mySnap == rightLine) {
bool left = (mySnap == leftLine);
double newW = (left ? w - mousePos.x : mousePos.x);
newW = widthDomain.closest(newW).safeDouble;
newW = apLockedDistance("w", newW.distance);
if (newW != w) {
if (left)
move( (w-newW, 0, 0).transformed(rot) );
w = newW;
apSetDistance("w", w.distance);
updateConnectors();
updateAfterStretch();
build2D();
invalidate();
}
return true;
} else if (mySnap == frontLine or mySnap == backLine) {
bool front = (mySnap == frontLine);
double newD = (front ? d - mousePos.y : mousePos.y);
newD = depthDomain.closest(newD).safeDouble;
newD = apLockedDistance("d", newD.distance);
if (newD != d) {
if (front)
move( (0, d-newD, 0).transformed(rot));
d = newD;
apSetDistance("d", d.distance);
updateConnectors();
updateAfterStretch();
build2D();
invalidate();
}
return true;
} else if (mySnap == heightSnap) {
double newH = mousePos.z;
newH = surfaceHeightDomain.closest(newH).safeDouble;
newH = apLockedDistance("h", newH.distance);
if (surfaceH != newH) {
surfaceH = newH;
apSetDistance("h", surfaceH.distance);
updateConnectors();
updateAfterStretch();
build2D();
invalidate();
}
return true;
}
return super(..);
}
When the user has released the cursor, the Snapper will recieve a call to the endStretch()
method. This is an appropriate time to run any wrap-up code that needs to happen after a stretch.
Note that the Animation will automatically detect if Connectors have become aligned after it has
completed. If any Connectors have become aligned, the Animation will attempt to connect them
(while still obeying the normal rules and restrictions for creating connections).

11
A. ConnectLines
ConnectLines are an especially common form of Connector, and they have several traits
unique to them. The most obvious is that they are a line, and not a single point. This allows for
multiple connections to be made, as opposed to the ConnectPoint, which only allows a single
connection. With the default implementation, a ConnectLine will assign a section of its line to be
used by anything that is connected to it. Any unused space on the line is available for more
connections.
An example of a ConnectLine being constructed may be seen in the code sample below.
Note that, unlike a ConnectPoint or Manipulator, an orientation is not specified. This is due to the
fact that the orientation of a ConnectLine can be inferred by the line itself. The orientation will be
right-perpendicular to the direction of the line. The figure below demonstrates this with the green
lines indicating the ConnectLines (pointing from start position to end position), and the blue lines
indicating their direction for accepting connections.
rightLine = ConnectLine(this, (w, 0, 0), (w, d, 0));
rightLine.setRule(dtDemoSideToSideRule);

ConnectLines can also be joined. If two ConnectLines:


· Are joinable() (which they are by default)
· Have identical orientations
· Have matching ConnectRules
· Meet at one of their terminal points
then they will be joined. When two ConnectLines are joined, they are treated as if they are
one line for the sake of incoming connections.
The edgeLimit() method tells the system whether or not an incoming connection should be
allowed to exceed an edge (one of the terminal points) of a ConnectLine. Note that this will not
come in to effect if the edge in question is joined to another ConnectLine. This method will be
passed three arguments: the ConnectLine that will be recieving the connection, the incoming
Connector that is attempting to align/connect to the ConnectLine, and an up flag that indicates
which edge the system is asking about (true indicates the edge at the ConnectLine’s end
position). This method should return true to indicate that the edge should be limited. For a visual
representation of edge limiting, see the figure below. The image shows possible placement
positions along the blue ConnectLine when it is edge limited (on left), and when it is not edge
limited (on right).

12
The allowLargerThanLine() method is used to indicate if another Snapper is allowed to
connect to the ConnectLine if the Snapper is larger than the ConnectLine. Note that this will not
come in to effect if the ConnectLine is not edge limited. The system will pass two arguments to
this method: the ConnectLine that will be recieving the connection, and the Connector that is
attempting to connect to it. The method should return true to indicate that the incoming Connector
should not be blocked from connection for being too large.
The connectLineConflicts() method is used to specify whether or not two Connectors (or
their Snappers) are “collidable” with each other on the same ConnectLine. This can be used to
allow Connectors to overlap on the ConnectLine. The example code below demonstrates
connectLineConflicts() being used to keep an under table pedestal and an office chair from
blocking each other. This allows both pedestals and chairs to connect to the table, without the need
for separate ConnectLines.
/**
* Connect line conflicts.
*/
public bool connectLineConflicts(ConnectLine myLine, Connector attach0,
Connector attach1) {
if ((attach0.snapper.DtDemoChair and attach1.snapper.DtDemoPedestal) or
(attach0.snapper.DtDemoPedestal and attach1.snapper.DtDemoChair))
return false;
return super(..);
}
connectLineAligns() is used to help the user place a Snapper at certain positions on the
ConnectLine. This method should return a tuple of three bools, indicating that the system should
help the user place the incoming Snapper at the left, center, or right positions. For example, if this
method returns <false, true, true>, the system will help the user place the Snapper at the
center or right position (depending on which is closer to the mouse cursor). If this method returns a
tuple of all false, the system will not help the user, and the incoming Snapper may be ’free-placed’
along all available positions on the ConnectLine.
connectLineAligns() will be passed three arguments: the ConnectLine that is recieving the
connection, the Connector that is attempting to connect to the ConnectLine, and the position of
the user’s cursor, expressed as a distance from the ConnectLine’s start position. The default
implementation returns a value of <true, true, true>, indicating that the system should help the
user place the Snapper at the left, right, or center position. In the code sample below, the
connectLineAligns() method is overriden to center anything connecting to the left or right side of
the Snapper, but free place anything connecting to the back.

13
/**
* Align a 'snap' to an 'attach' ConnectLine.
*/
public <bool, bool, bool> connectLineAligns(ConnectLine mySnap,
Connector attach, double defaultPos) {
if (mySnap == leftLine or mySnap == rightLine)
return <false, true, false>;
if (mySnap == backLine)
return <false, false, false>;
return super(..);
}
connectLineAlignConnector() serves the same purpose as connectLineAligns(), but
with more freedom. This method is passed the same arguments as connectLineAligns(), but
instead of returning a tuple, this method should return a desired position on the ConnectLine,
expressed as a distance from the start position. Note that this method’s default implementation will
call connectLineAligns(), so typically, overriding both is unnecessary.
Last, there is the snapAllocSize() method, which specifies how much space the Snapper
should consume when being connected to a ConnectLine. With the default implementation, the
Snapper’s size is assumed to be the size of the snapping ConnectLine (if it is a ConnectLine), or a
size based on the systemLocalBound(). When overridden, this method will be passed three
arguments: the Connector that is attempting to snap to the ConnectLine, the Connector this is
being snapped to (the argument is a Connector, but it will always be a ConnectLine), and the
nearest Connector on the ConnectLine (if any). The returned value should be a tuple of two
doubles, representing the size of the Snapper before and after the point of connection.

14

You might also like