########################################################
#
#               Alternate Texture Position Plugin
#
#
#                  by tiglari@hexenworld.met
#     
#
#   You may freely distribute modified & extended versions of
#   this plugin as long as you give due credit to tiglari &
#   Armin Rigo. (It's free software, just like Quark itself.)
#
#   Please notify bugs & possible improvements to
#   tiglari@hexenworld.net
#  
#
##########################################################

#$Header: /cvsroot/quark/runtime/plugins/maptexpin.py,v 1.3 2001/06/17 21:10:56 tiglari Exp $


Info = {
   "plug-in":       "Texture Pinning Plugin",
   "desc":          "Texture positioning by `pinning'",
   "date":          "Nov 18, 2000",
   "author":        "tiglari",
   "author e-mail": "tiglari@hexenworld.net",
   "quark":         "Version 6.x" }

import quarkx
import quarkpy.dlgclasses
import tagging
from quarkpy.maputils import *

#
#  - These utilities should go to qutils or some such place,
#    when this is prepared for stable release
#

#
# A utility for accessing attributes of objects
#  (class instances) that might not be defined
#
def getAttr(object, attr, default=None):
    if hasattr(object, attr):
        return getattr(object,attr)
    else:
        return default
#
# ditto for deleting attributes
#
def delAttr(object, attr):
    if hasattr(object, attr):
         delattr(object, attr)

def appendToAttr(object, attr, thing):
    if hasattr(object, attr):
        getattr(object,attr).append(thing)
    else:
        setattr(object,attr,[thing])

def removeFromAttr(object, attr, thing):
    getattr(object,attr).remove(thing)
    if getattr(object,attr)==[]:
        delattr(object,attr)



    
#
# Pinning dialog
#
#  ppdb's (quarkpy.dlgclasses) are set up like ordinary ones
#   except for an additional initialization parameter 'label'
#   that is used as a key for storing size/position info.
#
class VtxPinDlg(quarkpy.dlgclasses.placepersistent_dialogbox):
    #
    # dialog layout
    #

    endcolor = AQUA
    size = (140,140)
    dfsep = 0.30

    dlgdef = """
    {
        Style = "9"
        Caption = "Vertex Pinning"
        
        st:= { Txt = "st" Typ = "EF02"
               Hint = "Tex coordinates for this vertex, W, H." }
        
        pin:= { Txt = "pin:" Typ = "ER"
                   Hint = "# vertex being pinned (read-only)" }

        sep: = { Typ="S" Txt=""}

        exit:py = {Txt="" }
        cancel:py = {Txt="" }
    }"""

    def __init__(self,form,editor,label,msg,pos,face,facepins, repin):
       self.editor = editor
       self.src = quarkx.newobj(":")
       texp = face.threepoints(2)

       self.src["pin"] = msg
       
       self.oldtex=texCoords(pos, texp, 128)
       self.src["st"] = self.oldtex
       self.facepins = facepins
       self.pos = pos
       self.face = face
       self.repin = repin

       quarkpy.dlgclasses.placepersistent_dialogbox.__init__(self, form, self.src, label,
          cancel = quarkpy.qtoolbar.button(
            self.cancel,
            "Cancel dialog",
            ico_editor, 0,
            "Cancel"),
          exit = quarkpy.qtoolbar.button(
            self.exit,
            "Exit & apply changes",
            ico_editor, 2,
            "Exit & Apply")
        )

    def exit(self, dlg):
        quarkx.globalaccept()
        st = self.src["st"]
        st = st[0]/128, st[1]/128
        editor=self.editor
        pos = self.pos
        pinned = getAttr(editor,'pinned')
        facepins = self.facepins
        if pinned is None:
             pinned = editor.pinned = [(pos, st)]
        else:
             if self.repin:  # repin kludge
                 for pin in editor.pinned[:]: # don't remove from orig.
                     if not pin[0]-pos:
                         editor.pinned.remove(pin)
                         # could stop, but why not clearemall out?
             editor.pinned.append((pos,st))
             facepins.append((pos,st))
        numpins = len(facepins)
        if numpins == 3:
            face = self.face
            texp = solveForThreepoints(facepins[0], facepins[1], facepins[2])
            newface = face.copy()
            newface.setthreepoints(texp, 2)
            undo = quarkx.action()
            undo.exchange(face, newface)
            editor.ok(undo, "move texture")
        editor.invalidateviews()
        qmacro.dialogbox.close(self, dlg)


    def cancel(self, dlg):
          qmacro.dialogbox.close(self, dlg)


    
#
# New menu for vertexes
#
def vertexmenu(self, editor, view, oldmenu=quarkpy.maphandles.VertexHandle.menu.im_func):

    #
    # enable/disable info
    #
    pinned = getAttr(editor,'pinned')
    face = editor.layout.explorer.uniquesel
    if face is not None:
        if face.type!=":f":
            face = None
    facepins = []
    repin = 0
    if face is not None and pinned is not None:
        #
        # The idea here is that we only pay attention to
        # pins on the selected face; we can't set more
        # than three per face, but we can reset existing
        # ones.  Ugh messy.
        #
        def onface(pin, face=face, pos = self.pos):
            for cycle in face.vertices:
                for vertex in cycle:
                    if not (pin[0]-vertex):
                        return 1
            return 0
        facepins = filter(onface, pinned)
        #
        # The treatment of `repinning' seems awkward; it's removed
        #   from facepins, and replaced in editor.pinned if the
        #   dialog is executed.  Is there a cleaner way to do this?
        #
        for pin in facepins[:]:  # don't remove from original for loop
            if not pin[0]-self.pos:
                repin=1
                facepins.remove(pin)

    def pinClick(m, self=self, editor=editor, facepins=facepins, face=face,repin=repin):
        if len(facepins)==0:
            msg = "first"
        elif len(facepins)==1:
            msg = "second"
        elif len(facepins)==2:
            msg = "third"
        else:
            quarkx.msgbox('3 pins, clear some',MT_ERROR,MB_OK)
            return

        VtxPinDlg(quarkx.clickform, editor, 'vtxpin', msg, self.pos, face, facepins, repin)

    def clearPinClick(m, pos=self.pos, editor=editor, facepins=facepins):
        for pin in facepins[:]:
            if not (pos-pin[0]):
               facepins.remove(pin)
        for pin in editor.pinned[:]:
            if not pos-pin[0]:
                editor.pinned.remove(pin)
          
        editor.invalidateviews()
                
    def clearAllClick(m, editor=editor):
        delattr(editor,'pinned')
        editor.invalidateviews()
    
    if face is not None:
        stString = " (%.1f, %.1f)"%texCoords(self.pos, face.threepoints(2), 128) 
    else:
        stString = ""
    pinItem = qmenu.item('&Pin Vertex '+stString,pinClick,"|`pin' the texture on the face at the vertex. A face must be selected.\n\nAfter three pins have been set, the texture is repositioned so that the texture coordinates for the pinned points are as specified.")
    stItem = qmenu.item(stString, None,"No action")
    clearPin = qmenu.item('Clear Pin', clearPinClick)
    clearPins = qmenu.item('Clear All Pins', clearAllClick)
    
    clearPin.state= qmenu.disabled
    if pinned is None:
        clearPins.state = qmenu.disabled
    if (face is None) or (face.type != ':f'):
        pinItem.state = qmenu.disabled
        pinItem.hint = "|"+pinItem.hint + "\n\nThis menu item is disabled because it requires a face be selected"
    if repin:
        pinItem.text = 'Re&pin Vertex'
        clearPin.state = qmenu.normal
        
    #
    #  Promote/Demote of submenu seems like a good idea to
    #   apply to other submenus.
    #
    promote = quarkx.setupsubset(SS_MAP, "Options")["Pinners"]

    def promoteClick(m):
        quarkx.setupsubset(SS_MAP, "Options")["Pinners"]="1"
         
    def demoteClick(m):
        quarkx.setupsubset(SS_MAP, "Options")["Pinners"]="0"

    if promote=="1":
        promoteItem = qmenu.item('Demote Pinning',demoteClick, "|Pinning menu items get demoted to submenu")
    else:
        promoteItem = qmenu.item('Promote Pinning',promoteClick, "|Pinning menu items get promoted onto main vertex menu")

    pinList = [pinItem, clearPin, clearPins, promoteItem]
    
    if promote=="1":
        pinners = pinList
    else:
        pinners = [qmenu.popup('Texture Pinning', pinList)]


    return  pinners+oldmenu(self,editor,view)
    
quarkpy.maphandles.VertexHandle.menu = vertexmenu


#
# Now for drawing little boxes around pinned vertices
#
from tagging import drawredface # misnamed, oh well

def pinfinishdrawing(editor, view, oldmore=quarkpy.qbaseeditor.BaseEditor.finishdrawing):
      cv = view.canvas()
      try:
         pins = editor.pinned
      except (AttributeError):
         pass
      else:
         cv.pencolor = MapColor("Duplicator")
         for pin in pins:
             p1 = view.proj(pin[0])
             tagging.drawsquare(cv,p1,8)
      try:
         moving = editor.movingvertex
         p1 = view.proj(moving)
      except (AttributeError):
         pass
      else:
         if view.info["type"] == "3D":
              scalefactor = 50
         else:
             scalefactor = 30
         scale = view.scale(moving)
         for (color, axis) in (MapColor("Tag"), (1,0,0)), (MapColor("Bezier"), (0,1,0)),(MapColor("Duplicator"), (0,0,1)):
             cv.pencolor = color
             p0 = view.proj(moving)
             p1 = view.proj(moving+(scalefactor/scale)*quarkx.vect(axis))
             cv.line(p0, p1)
                 
#         else:
#             cv.pencolor = MapColor("Bezier")
#             tagging.drawsquare(cv,p1,8)
              

      cv.pencolor=MapColor("Duplicator")
      try:
          for face in editor.frozenFaces:
              drawredface(view,cv,face)
      except (AttributeError):
          pass
      
      oldmore(editor, view)

quarkpy.qbaseeditor.BaseEditor.finishdrawing = pinfinishdrawing

#$Log: #