title: Glueing
author: tiglari

Now on to implement the gluing command!  First, add a new
command to the  commands menu
called `&Glue to Tagged', which invokes the function glueSideClick.

Now to define this function.  It basically has to do three things:
<UL>
<LI>Identify the side that we want to glue (move)
<LI>Identify the side that we want to glue it to
<LI>Do the glue
</UL>

There's another, somewhat philsophical point about UI design,
which is that it's a good idea to have some separation between
the code implementing the UI and the code implementing the actual
operations that the UI is being used to control, since one often
wants to re-use the operations code elsewhere in the program.
It's often hard to figure out exactly how to split up the function,
but it's usually worth trying.  So here's the UI portion:

After getting the editor, we get the tagged face and the editor,
check that various preconditions are met, and then call the
glueing function we're going to define shortly:
<code>
def glueSideClick (m):
    editor = mapeditor()
    if editor is None: return
    tagged = editor.tagging.tagged
    #
    # get the selection (which will be a list)
    #
    sides = editor.layout.explorer.sellist
    #
    # check that it meets conditions (this could be done more slickly)
    #
    if len(sides) &lt; 1:
        quarkx.msgbox("Something must be selected for glueing", MT_WARNING, MB_OK)
        return
    if len(sides) > 1:
        quarkx.msgbox("Only one thing may be selected for glueing", MT_WARNING, MB_OK)
        return
    if sides[0].type != ":f":
        quarkx.msgbox("The selection must be a face", MT_WARNING, MB_OK)
    #
    # Now derive the new side
    #
    side = sides[0]
    newside = glueToTagged(side, tagged)
</code>
Now we get so something a bit tricky.  The glueToTagged function,
which we haven't written yet, is supposed to manufactory a copy
of the face `side', occupying the same plane as the face `tagged'.
But so far this face is just sitting there as the value of the
variable `newside', and we need to get it into the map.  There
is a special mechanism, the `undo' module, which should always
be used to make changes to the map, for the reason that the this
module keeps track of what's been done and enables it to be
undone and redone with the `Undo' menu items.

So here's how to use the undo module:
<code>
    # First create an `undo' object with the quarkx.action()
    #  function.  This will keep track of the actions performed.
    #
    undo = quarkx.action()
    #
    # Now substitute the new side for the old one (in the undo object)
    #
    undo.exchange(side, newside)
    #
    # and perform the action for real in the map
    #
    undo.ok(editor.Root, "glue to tagged")
</code>
Here we just perform one exchange before the ok, but any number
of actions can be performed, and there's more than just
exchange, as you can see by consulting
the documentation of <ref>src/quarkx/moduleundo</ref>.

Next we want to define the function that produces the new face.
This involves some map object manipulation and 3D math (see
<ref>src/quarkx/attributesface</ref>):
<code>
def glueToTagged(side, tagged):
    #
    # Make a copy of the original (so that texture etc. info is preserved)
    #
    new = side.copy()
    #
    # .distortion rotates the face into a new position; we have
    #  to make sure that the normal is going to point outward from
    #  its parent poly.
    #
    if new.normal*tagged.normal &lt; 0:
        new.distortion(-tagged.normal, new.origin)
    else: new.distortion(tagged.normal, new.origin)
    #
    #  Now shift our new side into position:
    #
    new.translate(tagged.origin-new.origin)
    return new
</code>
This code works, but it has various problems.  To see
the first, start a map, and click on the `Glue to Tagged' menu item.
Either nothing will happen at all, or else
you'll get a console-screen-full of verbiage ending in a complaint about
an `attribute error' (you're supposed to get the console error,
but currently something involved in delivering that error doesn't
always work).  This is because the GlueSideClick code refers to
the `tagging.tagged'-value of the mapeditor, but in this case it didn't
have one, because no `Tag' command had been issued.

Later we'll see how to fix this problem by having the menu item `disabled'
until a side is actually tagged, but since the problem of accessing
attributes that haven't been defined yet arises elsewere, we'll show
how to do it by using a function that contains an `exception handler':
<code>
def gettagged(o):
    try:
        return o.tagging.tagged
    except (AttributeError): return None
</code>

The `try' command runs the following block of code normally, except that
if the kind of error named in the following `except' statement occurs,
that execution is aborted, and the code in the except-block is
executed instead.  So this little function returns None rather than just
causing an error, in case whatever is fed to it lacks a
`tagging.tagged'-value.

So we can prevent the grotty-looking console outburst by replacing line
4 of GlueSideClick with:
<code>
tagged = gettagged(editor)
if tagged is None:
    quarkx.msgbox("Something must be tagged",MT_WARNING,MB_OK)
</code>
The real maptagside plugin actually does this, the gettagged function
and much else besides is defined in plugins/tagging.py (which should
perhaps be relocated to quarkpy, but hasn't been).  tagging.py
also defines a function tagface(face, editor) for setting the tag.

By now you might be wondering about the faces sometimes being
called `sides', this happened because `side' is the term I first
used when writing the plugin, and by the time I decided it
was a mistake, the bad decision had entrenched itself with
too many entanglements.

Returning to the mere functioning of our plugin, another problem is
that when we do tag a side, we see no indication of what we
have done, in unfavorable contrast to actual QuArK, where the tagged side
gets outlined in red, with a red square in the center.
And also the interface invites us to
perform actions that then produce error messages, such as trying to
glue when there's nothing to glue to, rather than indicating at
any moment what the sensible things to do are, and also there
are no hints or help.

Finally, the whole thing is rather clunky, we have to select faces, and then
go to the command menu to tag and glue them, and wouldn't it be better
if all this could be done off the right mouse-button menu?  In the next
three lessons we attend to these matters.
