#!BPY
# coding: utf-8
"""
Name: 'Astro'
Blender: 248
Group: 'Wizards'
Tip: 'Create stars from a data set'
"""
__author__ = "Keines"
__url__ = ['http://keines.gozaru.jp/blender/', 'http://blender.jp/', 'http://wiki.blender.org/index.php/Scripts/Manual/Misc/Astro']
__version__ = "2009.06.28(Sun) 01:03"

__bpydoc__ = """\

Note:
  Initially..
    .blender/scripts/Astro.py
    .blender/scripts/bpymodules/AstroUnit.py
    .blender/scripts/bpymodules/AstroDat.py
    .blender/scripts/bpydata/AstroDeg.dat

  Usage..
    Start Blender, go to Scripts Window
    Menu Scripts -> Misc -> Astro
    Edit following variables...
     - Year,Month,Day
     - Hour,Min,Sec
    Execute button, after a while,
    you'll see an sphere of stars in 3D Window.
    Enjoy!  :-) 
"""
"""
Python 2.5.2 / Ubuntu810 2.6.27-14-generic

History:
  2007.07.28(Sat) Beginning
    See Also Reference
    http://hooktail.org/computer/index.php?Gnuplot%A4%C7%A5%D7%A5%E9%A5%CD%A5%BF%A5%EA%A5%A6%A5%E0%A1%AA
    ftp://dbc.nao.ac.jp/DBC/NASAADC/catalogs/5/5050/
  2007.08.12(Sun) 00:33 Finished
  2008.08.02(Sat) 15:52 Summer version
  2008.11.25(Tue) 01:47 Brushup..
  2008.11.25(Tue) 23:12 Usage FileData:AstroDat.py -> AstroDeg.dat
  2009.02.18(Wed) 03:32 Ubuntu810 line:162-163
  2009.05.30(Sat) 23:52 Window.EditMode(0) -> Window.EditMode(0,undo_msg) T_T
  2009/06/25(Thu) 14:33 GUI !
  2009/06/27(Sat) 00:30 AstroDeg.dat, Text Window ok
  2009/06/28(Sun) 01:03 Blender.Get("udatadir")==None oh no...

CopyRight(c) 2009 K.Keina
Mail: keines.2007@gmail.com
Web : http://keines.gozaru.jp/blender/
"""
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****

import sys,re,math,time,os
from math import pi,cos,sin,atan,acos,asin,tan,floor,sqrt

from AstroUnit import *
from AstroDat import *

try:
  import Blender
  from Blender import Draw,BGL,Scene,Object,Window,Mesh,Mathutils,Material,Text
  from Blender.Mathutils import Matrix
  IsBlender=True
except:
  IsBlender=False

# Variables
Title		= 'Astro'
Author		= ' by '+__author__
VerWidth	= Draw.GetStringWidth(__version__+Author)
MainWidth	= VerWidth+Draw.GetStringWidth(Title)

# BaseValue
SpcWidth	= 20
BaseHeight	= 20
BtnHeight	= 20
Message 	= ''

BaseName=['Star.Line','Star','Star.Empty']
BaseNameLine=0
BaseNameStar=1
BaseNameEmpty=2

d24=(2*pi)/24.0
d36=(2*pi)/360.0

# Config
UserData	= [['Tokyo','139 45 00 E','35 39 00 N'],['Orion','05 20 00','+03 00 00']]
UserData_City	= 0
UserData_Star	= 1
AstroSize	= 20

FileName	= 'AstroDeg.dat'
FileData        = Blender.Get('udatadir')
if FileData==None:
  FileData = '.'
FileData	= FileData + '/' + FileName
#FileData='/home/keines/blender/Astro/AstroDeg.dat'
#FileData='E:/python/Astro/AstroDeg.dat'

BtnList	= [['Year:',0,Draw.Create(0),-10000,10000],['Month:',0,Draw.Create(0),1,12],['Day:',0,Draw.Create(0),1,31],['Hour:',0,Draw.Create(0),0,23],['Min:',0,Draw.Create(0),0,59],['Sec:',0,Draw.Create(0),0,59],['Longitude',0,Draw.Create(UserData[UserData_City][1]),0,0],['   Latitude',0,Draw.Create(UserData[UserData_City][2]),0,0],['Right',0,Draw.Create(UserData[UserData_Star][1]),0,0],[' Dec',0,Draw.Create(UserData[UserData_Star][2]),0,0],['Astro',0,Draw.Create(1),0,0],['Cursor',0,Draw.Create(0),0,0],['Edge',0,Draw.Create(0),0,0],['Now',0,0,0,0],['@',0,0,0,0],['Execute',0,0,0,0],['Path',0,0,0,0],['>>',0,Draw.Create(FileData),0,0],['X',0,0,0,0]]
BtnList_Year	= 0
BtnList_Month	= 1
BtnList_Day	= 2
BtnList_Hour	= 3
BtnList_Min	= 4
BtnList_Sec	= 5
BtnList_Longitude=6
BtnList_Latitude= 7
BtnList_Right	= 8
BtnList_Dec	= 9
BtnList_Astro	= 10
BtnList_Cursor	= 11
BtnList_Edge	= 12
BtnList_Now	= 13
BtnList_Menu    = 14
BtnList_Execute = 15
BtnList_File    = 16
BtnList_Path    = 17
BtnList_Exit    = 18

# Width
for i in range(len(BtnList)):
  BtnList[i][1]=Draw.GetStringWidth(BtnList[i][0]+str(BtnList[i][4]))+SpcWidth*2
BtnList[BtnList_Execute][1]=BtnList[BtnList_Now][1]
BtnList[BtnList_Exit][1]=BtnList[BtnList_Now][1]
BtnList[BtnList_Astro][1]=BtnList[BtnList_Cursor][1]
BtnList[BtnList_Edge][1]=BtnList[BtnList_Cursor][1]

MaxWidth=0
for i in range(3):
  BtnList[i+3][1]=BtnList[i][1]
  MaxWidth=MaxWidth+BtnList[i][1]+4
MaxWidth=MaxWidth+BtnList[BtnList_Now][1]

BtnList[BtnList_Path][1]=MaxWidth-BtnList[BtnList_File][1]-4

BtnList[BtnList_Menu][1]=SpcWidth
BtnList[BtnList_Longitude][1]=MaxWidth-SpcWidth-4
for i in range(3):
  BtnList[BtnList_Latitude+i][1]=BtnList[BtnList_Longitude][1]

# Event
Event_None	= 0
Event_ReDraw	= 1
Event_NowDate	= 2
Event_NowTime	= 3
Event_City	= 4
Event_Star	= 5
Event_Astro	= 6
Event_Cursor	= 7
Event_Edge	= 8
Event_Execute	= 9
Event_File	= 10
Event_Exit	= 11

def DrawGui():
    # --- Init Screen ---
    BGL.glClearColor(0.6, 0.6, 0.6, 0.0)
    BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT)
    BGL.glColor3f(0.1, 0.1, 0.15) # Text Color
    # --- Get Screen Size ---
    size=Blender.BGL.Buffer(Blender.BGL.GL_FLOAT, 4)
    Blender.BGL.glGetFloatv(Blender.BGL.GL_SCISSOR_BOX, size)
    size= size.list
    # FLOAT -> INT
    for s in range(4): size[s]=int(size[s])
    # Base Position
    x=20
    y=size[3]-BaseHeight
    # + Title
    BGL.glRasterPos2f(x, y)
    Draw.Text(Title)
    BGL.glRasterPos2f(x+MaxWidth-VerWidth-20, y)
    Draw.Text(__version__+Author)
    Draw.Button ("x",Event_Exit,x+MaxWidth-16,y-4,16,16,"")
    
    # Main Button
    y-=BaseHeight*2
    x=20
    # File    
    Draw.Button (BtnList[BtnList_File][0],Event_File,x,y,BtnList[BtnList_File][1],BaseHeight,"")
    BtnList[BtnList_Path][2]=Draw.String(BtnList[BtnList_Path][0], Event_None, x+BtnList[BtnList_File][1]+4, y, BtnList[BtnList_Path][1], BaseHeight,BtnList[BtnList_Path][2].val , 255)
    y-=BaseHeight*2
    # Date / Time
    h=BtnHeight+4
    yy=y-BtnHeight-4
    k=0
    for i in range(3):
      BtnList[i][2]=Draw.Number(BtnList[i][0],Event_None,x+k,y,BtnList[i][1], BtnHeight, BtnList[i][2].val, BtnList[i][3], BtnList[i][4])
      BtnList[i+3][2]=Draw.Number(BtnList[i+3][0],Event_None,x+k,yy,BtnList[i+3][1], BtnHeight, BtnList[i+3][2].val, BtnList[i+3][3], BtnList[i+3][4])
      k=k+BtnList[i][1]+4
    Draw.Button (BtnList[BtnList_Now][0],Event_NowDate,x+k,y,BtnList[BtnList_Now][1],BaseHeight,"")
    Draw.Button (BtnList[BtnList_Now][0],Event_NowTime,x+k,yy,BtnList[BtnList_Now][1],BaseHeight,"")
    y-=20
    # City / Star
    for i in range(2):
      y-=40
      k=(i*2)
      Draw.Button (BtnList[BtnList_Menu][0],Event_City+i,x,y,BtnList[BtnList_Menu][1],BaseHeight,"")
      Draw.String(BtnList[BtnList_Longitude+k][0]+': ',Event_None ,x+SpcWidth+4 ,y ,BtnList[BtnList_Longitude+k][1],BaseHeight,BtnList[BtnList_Longitude+k][2].val,255)
      y-=BaseHeight+4
      Draw.String(BtnList[BtnList_Latitude+k][0]+':  ',Event_None ,x+SpcWidth+4 ,y ,BtnList[BtnList_Latitude+k][1],BaseHeight,BtnList[BtnList_Latitude+k][2].val,255)
      y-=14
      BGL.glRasterPos2f(x+MaxWidth-Draw.GetStringWidth(UserData[i][0]), y)
      Draw.Text(UserData[i][0])
      if i==0:
        y-=40
        Draw.Toggle(BtnList[BtnList_Astro][0],Event_Astro,x,y,BtnList[BtnList_Edge][1],BtnHeight,BtnList[BtnList_Astro][2].val)
        Draw.Toggle(BtnList[BtnList_Cursor][0],Event_Cursor,x+BtnList[BtnList_Cursor][1],y,BtnList[BtnList_Cursor][1]+4,BtnHeight,BtnList[BtnList_Cursor][2].val)
        Draw.Toggle(BtnList[BtnList_Edge][0],Event_Edge,x+BtnList[BtnList_Cursor][1]*2+4,y,BtnList[BtnList_Edge][1],BtnHeight,BtnList[BtnList_Edge][2].val)
        Draw.Button (BtnList[BtnList_Execute][0],Event_Execute,x+MaxWidth-BtnList[BtnList_Execute][1],y,BtnList[BtnList_Execute][1],BaseHeight,"")
      if BtnList[BtnList_Cursor][2].val==0:
        break
    BGL.glRasterPos2f(x, y-30)
    Draw.Text(Message)

def ButtonEvent(evt):
    if (evt==Event_Exit)	: Draw.Exit()
    elif (evt==Event_ReDraw)	: Draw.Register(DrawGui, KeyEvent, ButtonEvent)
    elif (evt==Event_File)	: Window.FileSelector (FileSelectFunction,'Select File',BtnList[BtnList_Path][2].val)
    elif (evt==Event_NowDate)	: NowDate(0,3,True)
    elif (evt==Event_NowTime)	: NowDate(3,6,True)
    elif (evt==Event_City)	: MenuCity()
    elif (evt==Event_Star)	: MenuStar()
    elif (evt==Event_Execute)	: AstroExecute()
    elif (evt in [Event_Astro,Event_Cursor,Event_Edge]): 
      BtnList[BtnList_Astro+(evt-Event_Astro)][2].val=not BtnList[BtnList_Astro+(evt-Event_Astro)][2].val
      Draw.Register(DrawGui, KeyEvent, ButtonEvent)

def FileSelectFunction(fn):
    BtnList[BtnList_Path][2].val=fn
    Draw.Register(DrawGui, KeyEvent, ButtonEvent)

def NowDate(s,e,IsDraw):
    tm=time.localtime(time.time()) # (2009, 6, 25, 5, 21, 13, 3, 176, 0)
    for i in range(s,e):
      BtnList[i][2].val=tm[i]
    if IsDraw:
      Draw.Register(DrawGui, KeyEvent, ButtonEvent)

def KeyEvent(evt, val):
    # val: 1=KeyDown 0=KeyUp
    if (not val) :
        return
    if evt in [Draw.QKEY]:
        Draw.Exit()

def MenuCity():
    global Message
    MenuList=[(k[0]) for k in CityDat]
    if len(MenuList)==0:
      Message='"AstroDat.py" does not exist.'
    else:
      work='Select City%t'
      ss='|%s %%x%d'
      for i,s in enumerate(MenuList):
        work+=ss%(s,i)
      i=Draw.PupMenu(work,10)
      if i!=-1:
        UserData[UserData_City][0]=CityDat[i][0]
        UserData[UserData_City][1]=CityDat[i][1]
        UserData[UserData_City][2]=CityDat[i][2]
        BtnList[BtnList_Longitude][2].val=CityDat[i][1]
        BtnList[BtnList_Latitude][2].val=CityDat[i][2]
    Draw.Register(DrawGui, KeyEvent, ButtonEvent)

def MenuStar(): 
    global Message
    MenuList=[(k[0]) for k in StarDat]
    if len(MenuList)==0:
      Message='"AstroDat.py" does not exist.'
    else:
      work='Select Star%t'
      ss='|%s %%x%d'
      for i,s in enumerate(MenuList):
        work+=ss%(s,i)
      i=Draw.PupMenu(work,20)
      if i!=-1:
        UserData[UserData_Star][0]=StarDat[i][0]
        UserData[UserData_Star][1]=StarDat[i][1]
        UserData[UserData_Star][2]=StarDat[i][2]
        BtnList[BtnList_Right][2].val=StarDat[i][1]
        BtnList[BtnList_Dec][2].val=StarDat[i][2]
    Draw.Register(DrawGui, KeyEvent, ButtonEvent)

# Longitude WN to WE
def ConvWest(s,v):
  if re.search("[wW]", s):
    v*=-1
  elif re.search("[eE]", s):
    v=360-v
  return v

def AstroMake(f,TM,Lo,la):
  global Message
  # + Stellar data
  Message = "'" + FileName + "'"
  IsFind=False
  try:
    # User Path
    f = file(f)
    f.readline()
    Message = 'User Path ' + Message
  except IOError, err:
    # Text Window
    d=Text.Get()
    for dat in d:
      if dat.getName()==FileName:
        IsFind=True
        break
    if IsFind:
      try:
        f=dat.asLines()
        f.pop(0)
        Message = 'Text Window ' + Message
      except IOError, err:
        IsFind=False
        None
    if not IsFind:
      Message = 'Unable to open file ' + Message
      Draw.Register(DrawGui, KeyEvent, ButtonEvent)
      return
  Draw.Register(DrawGui, KeyEvent, ButtonEvent)
  
  L=rdAngle(Lo)		# dms:Longitude
  l=rdAngle(la)		# dms:Latitude
  LW=ConvWest(Lo,L)
  work='-'
  
  vList={}
  rexp=re.compile("[^\t\n]+")
  for i,w in enumerate(f):
    w=rexp.findall(w)
    if len(w)==0: break
    r=float(w[0])  # RA
    d=float(w[1])  # DE
    # + r,d decimal->rad
    x,y,z,A,a=horizontal(TM,JD,r*d24,d*d36,LW,l,TM[3],TM[4],TM[5])
    x*=-AstroSize
    y*=AstroSize
    z*=AstroSize
    #if z>-1: # Ground Under..
    if not vList.has_key(w[2]): vList[w[2]]={}			# Vmag
    if not vList[w[2]].has_key(w[3]): vList[w[2]][w[3]]=[]	# Spec
    vList[w[2]][w[3]].append([x,y,z])
  if not IsBlender:return
  # + Make Object
  editmode = Window.EditMode()
  if editmode: Window.EditMode(0,Title)
  # value
  for v1 in vList.iteritems():
    # color
    for v2 in v1[1].iteritems():
      # Have Object ?
      IsBool=True
      name=BaseName[BaseNameStar]+'['+v1[0]+'_'+v2[0]+']'
      try:
        ob = Blender.Object.Get(name)
        me=ob.getData(mesh=1)
        if me!=None:
	  # once reset..
          me.verts=None
          IsBool=not IsBool
      except:
        None
      if IsBool:
        me = Mesh.New(name)
        scn = Scene.GetCurrent()
        ob = scn.objects.new(me, name) # Vmag_Spec
      
      me.verts.extend(v2[1])
      
      # + Material
      try:
        mat=Material.Get(ob.name)
      except:
        mat = Material.New(ob.name)
      # + spec ABCFGKMNOpSW
      #mat.rgbCol=Spec2RGB(v2[0]) # ubuntu810 :oh no..
      mat.rgbCol=Spec2RGB(v2[0][0])
      
      # Material Property
      v=float(v1[0])
      mat.mode |= Material.Modes.HALO
      mat.haloSize=Vmag2Size(v)
      me.materials=[mat]
      me.update()
  
  Blender.Window.RedrawAll()
  if editmode: Window.EditMode(1)

# right ascension:0..24   celestial declination:-90..90
def LineMake(TM,Lo,la):
  # + Preferences
  v=[]			# Vertexes
  e=[]			# Edge
  n={}			# Right Ascension Index
  k=0			# Count
  RA=24
  DE=9
  DEd=10
  L=rdAngle(Lo)		# dms:Longitude sexagesimal number
  l=rdAngle(la)		# dms:Latitude  sexagesimal number
  LW=ConvWest(Lo,L)
  
  # + line edge
  #   - Right ascension loop
  for ra in range(RA) :			 # RA0..23
    # - Declination loop
    IsAppend=False
    for dc in range(-8,9) : 		# -8, 0..18
      r=rdAngle(str(ra)+' 00 00')	# hms:Right ascension
      d=rdAngle(str(dc*DEd)+' 00 00')	# dms:Declination
      #   - r,d 24,36 rad
      x,y,z,A,a=horizontal(TM,JD,r*d24,d*d36,LW,l,TM[3],TM[4],TM[5])
      x*=-AstroSize
      y*=AstroSize
      z*=AstroSize
      #if z>=0:				# Underground
      IsAppend=True
      v.append([x,y,z])			# Vertex
      e.append([k,k+1])			# Declination Edge
      n[ra,dc]=len(v)
      k+=1
    if IsAppend: del e[len(e)-1]	# Last Edge
  if not IsBlender : return
  
  # + Right ascension edge
  w=[0,0]
  for r in range(RA-2) :		# RA
    for d in range(-8,9):		# -8,9
      if n.has_key((r,d)):
        if n.has_key((r+1,d)):
	    e.append([n[r,d] ,n[r+1,d]])
  #   - first and last
  r=RA-2
  for d in range(-8,9):
    if n.has_key((0,d)):
      if n.has_key((r,d)):
        e.append([n[0,d] ,n[r,d]])
  # + Make Object(Edge)
  editmode = Window.EditMode()
  if editmode: Window.EditMode(0,Title)
  
  # Have Object ?
  IsBool=True
  try:
    ob = Blender.Object.Get(BaseName[BaseNameLine])
    me=ob.getData(mesh=1)
    if me!=None:
      me.verts=None
      IsBool=not IsBool
  except:
    None
  
  if IsBool:
    me = Mesh.New(BaseName[BaseNameLine])
    scn = Scene.GetCurrent()
    ob = scn.objects.new(me, BaseName[BaseNameLine])
  
  me.verts.extend(v)
  me.faces.extend(e)
  
  # Have Material ?
  try:
    mat=Material.Get(BaseName[BaseNameLine])
  except:
    mat = Material.New(ob.name)
  # Material Property
  mat.mode |= Material.Modes.WIRE | Material.Modes.ZTRANSP | Material.Modes.SHADELESS
  mat.alpha = 0.65
  mat.emit = 1.0
  mat.rgbCol=[0.393,0.690,1.0]
  me.materials=[mat]
  me.update()
  
  Blender.Window.RedrawAll()
  if editmode: Window.EditMode(1)
  #Blender.Window.DrawProgressBar(1, '')

# 3D Cursor
def Indicate(TM,ra,dc,Lo,la): # lon,lat,dat,h,m,s
  # + Preferences
  #   - Converted to decimal
  L=rdAngle(Lo)		# dms Longitude
  l=rdAngle(la)		# dms Latitude
  r=rdAngle(ra)		# hms Right ascension
  d=rdAngle(dc)		# dms Declination
  LW=ConvWest(Lo,L)
  # + Coordinate transformation
  x,y,z,A,a=horizontal(TM,JD,r*d24,d*d36,LW,l,TM[3],TM[4],TM[5])
  x*=-AstroSize
  y*=AstroSize
  z*=AstroSize
  # + Move 3D Cursor
  Blender.Window.SetCursorPos(x,y,z)
  try:
    ob=Object.Get(BaseName[BaseNameEmpty])
    ob.setLocation (x, y, z)
  except:
    None
  Window.Redraw(Window.Types["VIEW3D"])

def AstroExecute():
    global Message,JD
    Message=''
    
    BaseTime=[BtnList[i][2].val for i in range(6)]
    JD=JulianDay(BaseTime[0],BaseTime[1],BaseTime[2])
    
    # Star
    if BtnList[BtnList_Astro][2].val==1:
      AstroMake(BtnList[BtnList_Path][2].val,BaseTime,BtnList[BtnList_Longitude][2].val,BtnList[BtnList_Latitude][2].val)
    
    # 3D Cursor
    if BtnList[BtnList_Cursor][2].val==1:
      Indicate(BaseTime,BtnList[BtnList_Right][2].val,BtnList[BtnList_Dec][2].val,BtnList[BtnList_Longitude][2].val,BtnList[BtnList_Latitude][2].val)
    
    # Edge Longitude Latitude
    if BtnList[BtnList_Edge][2].val==1:
      LineMake(BaseTime,BtnList[BtnList_Longitude][2].val,BtnList[BtnList_Latitude][2].val)
    
NowDate(0,6,False)
Draw.Register(DrawGui, KeyEvent, ButtonEvent)