#######################
#-*- coding: utf-8 -*-


######## DM2
######## agruber@tugraz.at and iam@richdank.com
######## library



################
######## basic library data and functions
################

######## credits
lib_cc = "DM2 LIBRARY ~~~~~~~~~~~~~~~~~~~"
lib_cc += "\n    2022-11-29 10:00:00     ### ver w22_03 ###"
lib_cc += "\n    CC by-nc-sa "
lib_cc += "diag & richDank" 

#######################

######## import
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import RhinoPython.Host as _host
from datetime import datetime
import random
import time
import math
from itertools import combinations
from itertools import permutations
from itertools import product
import System, System.DateTime
import scriptcontext

#from ghpythonlib import treehelpers
#import System
pi = math.pi
global timeX
timeX = time.time()

def setTime():
    global timeX
    timeX = time.time()
#setTime()


def getTime(verbose=0, prenull=0):
    global timeX
    tim = float(time.time())
    tim -=  float(timeX)
    if verbose: print str(round(tim,2))+"secs"
    #return preNull( round(tim,1), prenull)
    return round(tim,1)
################
######## functions / setUPs
################

######## DM2_w21

### hu_02 PARALL.EL.EPIPED
lenX = 10       
lenY = random.randint(lenX, lenX*4)
lenZ = random.randint(lenX, lenX*9)
#print "\t _____________/################"
#print "\t my randomized para.llel.epiped"
#print "\t X/Y/Z =", str(lenX)+"/"+str(lenY)+"/"+str(lenZ)+" _____________"
#print "\t ################/  "

def getParalellEpipedCoords(randVec=0, drawPoints=0):
    lenX = 10       
    lenY = random.randint(lenX, lenX*4)
    lenZ = random.randint(lenX, lenX*9)

    print "\t _____________/################"
    print "\t my randomized para.llel.epiped"
    print "\t X/Y/Z =", str(lenX)+"/"+str(lenY)+"/"+str(lenZ)+" _____________"
    print "\t ################/  "


    #______base vectors
    vecX = [1, 0, 0]
    vecY = rs.VectorUnitize([ random.uniform(0,0.5), random.uniform(0.5,1), 0 ])
    vecZ = rs.VectorUnitize([ random.uniform(-0.5,0.5), random.uniform(-0.5,0.5), 1 ])
    rs.ObjectColor( rs.AddCurve([ [0,0,0], rs.VectorScale(vecX, lenX) ] ), [200,0,0] )
    rs.ObjectColor( rs.AddCurve([ [0,0,0], rs.VectorScale(vecY, lenY) ] ), [0,200,0] )
    rs.ObjectColor( rs.AddCurve([ [0,0,0], rs.VectorScale(vecZ, lenZ) ] ), [0,0,200] )
    rs.AddPoints( [rs.VectorScale(vecX, lenX), rs.VectorScale(vecY, lenY), rs.VectorScale(vecZ, lenZ)] )
    #______here comes my para.llel.epiped:
    allCoords = []
    for x in range(lenX+1):
        for y in range(lenY+1):
            for z in range (lenZ+1):
                if randVec:
                    x=random.uniform(0, lenX)
                    y=random.uniform(0, lenY)
                    z=random.uniform(0, lenZ)
                cor = rs.VectorAdd( rs.VectorAdd( rs.VectorScale( vecX, x), rs.VectorScale( vecY, y)), rs.VectorScale( vecZ, z) )  
                allCoords.append( cor )
    if drawPoints:
        rs.AddPoints( allCoords )
    return allCoords
    

### hu_03 SPHERI.FICATION
#print "\t   _____      ____"
#print "\t  |     | >  /   .\\"
#print "\t  |     | < /    . \\   sphere"
#print "\t  |     | > |    . |       if"
#print "\t  |     | > \\      /  ication"
#print "\t  |_____| <  \____/"
def coords2sphere ( coords, center, radius, makePts=1, makeCrv=0, makeProjectCrv=0 ):


    if center in coords:
        coords.remove( center )
    coordsOnSphere=[]
    for vec in coords:
        cor = vec
        if not rs.Distance( vec, center ):
            continue
        vec = rs.VectorSubtract( vec, center ) # == vec center..cor
        vec = rs.VectorUnitize( vec )
        vec = rs.VectorScale( vec, radius )
        vec = rs.VectorAdd( center, vec)
        coordsOnSphere.append( vec )
        if makeProjectCrv==1:
            rs.AddLine( center, vec )
        if makeProjectCrv==2:
            rs.AddLine( cor, vec )
    if makePts:
        rs.ObjectColor(rs.AddPoints( coordsOnSphere ), [10,10,10] )
    if makeCrv:
        rs.AddCurve( coordsOnSphere, makeCrv )
    return coordsOnSphere


### hu_04 TETRAEDER
#print "                            /\\       "
#print "                           /_|\\      "
#print "                      TETR/__|_\\EDER _01"
#print "........................./   |  \\....... h = a/3.0*(6.0**0.5)\n"

pointsX = [ [0, 30, 0], [50, 0, 0], [30, 20, 0] ]
pointsX = [ [0, random.uniform(10,50), 0], [random.uniform(10,50), random.uniform(10,50), random.uniform(10,50)], [random.uniform(10,50), random.uniform(10,50), 0] ]
def getTetraCoords( points=pointsX, flipH=0, flipAB=0, drawTetra=1, drawAll=1 ):
    ptA=points[0+flipAB]
    ptB=points[1-flipAB]
    ptX=points[2]
    normVec = rs.VectorCrossProduct( rs.VectorSubtract(ptX, ptA), rs.VectorSubtract(ptX, ptB) )
    a = rs.Distance(ptA, ptB)
    h = a/3.0*(6.0**0.5)*(1-2*flipH)       ### "a drittel mal wurzel aus 6"
    vecH = rs.VectorScale( rs.VectorUnitize( normVec ), h )
    vec_AC = rs.VectorRotate(rs.VectorSubtract( ptB, ptA ), 60.0, vecH)
    ptC = rs.VectorAdd(ptA, rs.VectorRotate(rs.VectorSubtract( ptB, ptA ), 60.0, vecH))
    ptH = pntCentroid( [ptA, ptB, ptC] )
    ptT = rs.VectorAdd( ptH, vecH )
    tetraCoords = [ [ptA, ptB, list(ptC)], [ptA, ptB, list(ptT)], [ptB, list(ptC), list(ptT)], [list(ptC), ptA, list(ptT)] ]
    if drawTetra:
        for coords in tetraCoords:
            rs.AddCurve( coords, 1 )
    if drawAll:
        rs.AddPoints( [ptA, ptB, ptX, ptH] )
        rs.AddLine( ptH, ptT )
        rs.ObjectColor( rs.AllObjects()[0:5], [100,100,0] )
        print "***************"
        print "*** points     ",points
        print "*** edgeLength ", rs.Distance(ptA, ptB)
        print "*** tetraHeight", h
        print "***************"
    if drawAll==2:
        textDots([ptA], "A", 1)
        textDots([ptB], "B", 1)
        textDots([ptX], "X", 1)
        rs.ObjectColor( rs.AllObjects()[0:3], [100,100,0] )
    return tetraCoords

def getUnoCoord( l, d, h ):
    if l >= L or d >= D or h >= H:
        print "*** getUnoCoord: any parameter too big:",l,d,h,"? ______________***"
        #return UnoGridCoords[ 0 ]
        return [0,0,0]
    else:
        return UnoGridCoords[ l + d*L + h*D*L ]

UnoGridCoords = []  # just 4 check in hu_05_basic_script_structure
                    # diag 201107_00:00

################
######## functions 4duerer 2020
######## functions cage 4_corona 2021
################



def getLodge( p_width=1.2, l_dist=10, d_dist=8, h_dist=6, pos_x=0, pos_y=0, d_grid=0, d_distord=1, demo=0, verbose=0 ) :
    """
    Generate a small archetypal house, distorted along perspective algorithms
    A setup for the development of individual panels
    Inspired by
      Albrecht Duerer's "Underweysung" (1525)
      https://de.wikipedia.org/wiki/Perspektive#Geschichte
      https://en.wikipedia.org/wiki/Albrecht_Duerer
    And
      MVRDVs "Porterlodges for the National Park De Hoge Veluwe" (1996)
      https://mvrdv.com/projects/167/hoenderloo-lodge?photo=15877
      https://www.miesarch.com/work/2994
    Parameters:
      p_width (float, optional): Approx. panel width in units
      l_dist (float, optional): Approx. length of the lodge in units
      d_dist (float, optional): Approx. depth of the lodge in units
      h_dist (float, optional): Approx. height of the lodge in units
      x_pos (float, optional): Move origin of the lodge in x
      y_pos (float, optional): Move origin of the lodge in y
      d_grid (boolean, optional): Fixed depth grid on the the small sides
      d_distord (boolean, optional): Distord basic section of the house
      demo (boolean, optional): Run demo to get an object
      verbose (int, optional): talk & sleep / Print and depict generation of the lodge
    Returns:
      list(float): list[][][] of all panels, containing groups of 4 points
      list(float): list[][][] of just the sleeve's panels
      list(float): list[][][] of just the right section's panels
      list(float): list[][][] of just the left section's panels
      list(float): list[][][] of the sections, containing groups of 5 points
      list(float): list[][] of all the cvs on the sleeve
      list(float): list[][] of all the cvs on the right section
      list(float): list[][] of all the cvs on the left section
    Example:
      import dm2_lib as dm2
      all_data = dm2.getLodge( verbose=1000, demo=1 )
      panels = all_data[0]
      panels_sleeve = all_data[1]
      panels_right = all_data[2]
      panels_left = all_data[3]
      sections = all_data[4]
      cvs_sleeve= all_data[5]
      cvs_right= all_data[6]
      cvs_left= all_data[7]
    """
    if 0 or verbose:
        print "                      ________"
        print "                     /_/_/_|__\\"
        print "### dmLODGE          |_|_|_|__|\n###########################2022 all data returned 2 allLodgeData[]"
    print "*** allLodgeData  = [ panels, panels_sleeve, panels_right, panels_left, sections, cvs_sleeve, cvs_right, cvs_left ]"
    if verbose:
        rs.ObjectColor( rs.AddCurve( [[0,0,0], [pos_x, pos_y, 0]] ), [200,0,20] )
        #rs.ZoomExtents()
    ######## focal point
    focus = [ 
        (-1)**random.randint(0,1) * random.uniform( l_dist*4, l_dist*6 )
        , random.uniform( -d_dist*2, d_dist*2 )
        , 0 
        ]
    ######## planes to project on
    plane0 = rs.PlaneFromNormal(
        [ random.uniform(l_dist/2,l_dist/3), 0, 0 ]
        , [ 3, random.uniform(-1,1), random.uniform(-1,1) ]
        )
    plane1 = rs.PlaneFromNormal(
        [ -random.uniform(l_dist/2,l_dist/3), 0, 0 ]
        , [ 3, random.uniform(-1,1), random.uniform(-1,1) ]
        )

    ######## standard section
    section0 = []
    section1 = []
    if d_distord :
        a = [ 0, random.uniform(-d_dist/2,-d_dist/3), 0 ]
        b = [ 0, random.uniform(-d_dist/2,-d_dist/3), random.uniform(h_dist/3,h_dist/2) ]
        c = [ 0, random.uniform(-d_dist/6,d_dist/6), random.uniform(h_dist/2,h_dist) ]
        d = [ 0, random.uniform(d_dist/3,d_dist/2), random.uniform(h_dist/3,h_dist/2) ]
        e = [ 0, random.uniform(d_dist/3,d_dist/2), 0 ]
    else :
        a = [ 0, -d_dist/2, 0 ]
        b = [ 0, -d_dist/2, random.uniform(h_dist/3,h_dist/2) ]
        c = [ 0, 0, random.uniform(h_dist/2,h_dist) ]
        d = [ 0, d_dist/2, random.uniform(h_dist/3,h_dist/2) ]
        e = [ 0, d_dist/2, 0 ]
    section = [ a, b, c, d, e ]
    ### section_id = rs.AddCurve( section, 1 );
    if verbose : rs.Sleep( verbose )
    ######## get all the cvs alongside (sleeve)
    cvs_sleeve = []
    subdiv_l = int( rs.Distance(plane0[0],plane1[0]) / p_width )
    if verbose : print "the long sides / the sleeve will be separated in", subdiv_l, "segments."

    for pnt in section :
        ######## optic rays
        vec = rs.VectorSubtract( pnt, focus )
        end = rs.VectorAdd( pnt, vec )
        crv_id = rs.AddLine( focus, end )
        ######## projected on the planes
        pierce0 = rs.PlaneCurveIntersection( plane0, crv_id )[0][1]
        pierce1 = rs.PlaneCurveIntersection( plane1, crv_id )[0][1]
        section0.append( pierce0 )
        section1.append( pierce1 )
        #if verbose : rs.ZoomExtents()
        if verbose :
            foc_id = rs.AddPoint( focus )
            pnt_id = rs.AddPoint( pnt )
            ##rs.AddCircle( plane0, max(d_dist,l_dist,h_dist) )
            ##rs.AddCircle( plane1, max(d_dist,l_dist,h_dist) )
            section0_ids = rs.AddPoints( section0 )
            section1_ids = rs.AddPoints( section1 )
            rs.Sleep( verbose )
            rs.DeleteObjects( section0_ids )
            rs.DeleteObjects( section1_ids )
            rs.DeleteObject( foc_id )
            rs.DeleteObject( pnt_id )
            #rs.ZoomExtents() ###
        #if verbose : rs.ZoomExtents() ###
        rs.DeleteObject( crv_id )
        ######## subdivide the edges alongside (sleeve)
        for s in range(subdiv_l+1):
            fac = float(s) / subdiv_l
            coord = pntInbetween( pierce0, pierce1, fac )
            cvs_sleeve.append( coord )
    #if verbose : rs.ZoomExtents() ###
    sections = [ section0, section1 ]
    ######## get the sections alongside (sleeve)
    sections = []
    for u in range( subdiv_l+1 ) :
        pnts = []
        for v in range( 5 ) :
            pnts.append( cvs_sleeve[(v+0)*(subdiv_l+1)+(u+0)] )
        sections.append( pnts )
    ######## get the panels alongside (sleeve)
    panels_sleeve = []
    for u in range( subdiv_l ) :
        for v in range( 5-1 ) :
            pnt0 = cvs_sleeve[ (v+0)*(subdiv_l+1)+(u+0) ]
            pnt1 = cvs_sleeve[ (v+0)*(subdiv_l+1)+(u+1) ]
            pnt2 = cvs_sleeve[ (v+1)*(subdiv_l+1)+(u+1) ]
            pnt3 = cvs_sleeve[ (v+1)*(subdiv_l+1)+(u+0) ]
            panel = [ pnt0, pnt1, pnt2, pnt3 ]
            panels_sleeve.append( panel )
    ######## general subdivision for the sections
    subdiv_d = int(round( rs.Distance(a,e) / p_width / 2 )) * 2
    ######## end section right cvs
    cvs_right = []
    subdiv_d0 = int( rs.Distance(section0[0],section0[4]) / p_width )
    fac_d0a = rs.Distance(section0[1],section0[2])/(rs.Distance(section0[1],section0[2])+rs.Distance(section0[3],section0[2]))
    subdiv_d0a = int(round( subdiv_d0 * fac_d0a ))
    subdiv_d0b = subdiv_d0 - subdiv_d0a
    if d_grid :
        subdiv_d0 = subdiv_d
        subdiv_d0a = int( subdiv_d/2 )
        subdiv_d0b = int( subdiv_d/2 )
    if verbose : print "the section on the right is devided in", subdiv_d0a, "and", subdiv_d0b, "panels."
    for p in range(subdiv_d0+1) :
        fac = float(p) / subdiv_d0
        coord = pntInbetween( section0[4], section0[0], fac )
        cvs_right.append( coord )
    for p in range(subdiv_d0+1) :
        if ( p<subdiv_d0b ) :
            fac = float(p) / subdiv_d0b
            coord = pntInbetween( section0[3], section0[2], fac )
            cvs_right.append( coord )
        else :
            fac = (float(p)-subdiv_d0b) / subdiv_d0a
            coord = pntInbetween( section0[2], section0[1], fac )
            cvs_right.append( coord )
    ######## get the panels on end section right
    panels_right = []
    for p in range(subdiv_d0) :
        pnt0 = cvs_right[ (p+0) ]
        pnt1 = cvs_right[ (p+1) ]
        pnt2 = cvs_right[ (p+1)+(subdiv_d0+1) ]
        pnt3 = cvs_right[ (p+0)+(subdiv_d0+1) ]
        panel = [ pnt0, pnt1, pnt2, pnt3 ]
        panels_right.append( panel )
    ######## end section left cvs
    cvs_left = []
    subdiv_d1 = int( rs.Distance(section1[0],section1[4]) / p_width )
    fac_d1a = rs.Distance(section1[3],section1[2])/(rs.Distance(section1[1],section1[2])+rs.Distance(section1[3],section1[2]))
    subdiv_d1a = int(round( subdiv_d1 * fac_d1a ))
    subdiv_d1b = subdiv_d1 - subdiv_d1a
    if d_grid :
        subdiv_d1 = subdiv_d
        subdiv_d1a = int( subdiv_d/2 )
        subdiv_d1b = int( subdiv_d/2 )
    if verbose : print "the section on the other end is split in", subdiv_d1a, "and", subdiv_d1b, "panels."
    for p in range(subdiv_d1+1) :
        fac = float(p) / subdiv_d1
        coord = pntInbetween( section1[0], section1[4], fac )
        cvs_left.append( coord )
    for p in range(subdiv_d1+1) :
        if ( p<subdiv_d1b ) :
            fac = float(p) / subdiv_d1b
            coord = pntInbetween( section1[1], section1[2], fac )
            cvs_left.append( coord )
        else :
            fac = (float(p)-subdiv_d1b) / subdiv_d1a
            coord = pntInbetween( section1[2], section1[3], fac )
            cvs_left.append( coord )
    ######## get the panels on end section right
    panels_left = []
    for p in range(subdiv_d1) :
        pnt0 = cvs_left[ (p+0) ]
        pnt1 = cvs_left[ (p+1) ]
        pnt2 = cvs_left[ (p+1)+(subdiv_d1+1) ]
        pnt3 = cvs_left[ (p+0)+(subdiv_d1+1) ]
        panel = [ pnt0, pnt1, pnt2, pnt3 ]
        panels_left.append( panel )
    ######## all the panels
    panels = []
    panels.extend( panels_sleeve )
    panels.extend( panels_right )
    panels.extend( panels_left )
    ######## display features
    if verbose: section0_id = rs.AddCurve( section0, 1 );
    if verbose: section1_id = rs.AddCurve( section1, 1 );
    
    #if verbose : rs.ZoomExtents() ###
    ### rs.ObjectColor( rs.AllObjects()[:2], [100,200,100])
    ### rs.DeleteObject( section_id )
    if verbose : 
        rs.Sleep( verbose )
        cvs_ids = rs.AddPoints( cvs_sleeve )
        rs.Sleep( verbose*1 ) ## was *2 ag
        rs.DeleteObjects( cvs_ids )
        cvs_ids = rs.AddPoints( cvs_right )
        rs.Sleep( verbose*1 ) ## was *2 ag
        rs.DeleteObjects( cvs_ids )
        cvs_ids = rs.AddPoints( cvs_left )
        rs.Sleep( verbose*1 ) ## was *2 ag
        rs.DeleteObjects( cvs_ids )
        rs.DeleteObjects( [section0_id,section1_id] )
    ######## move lodge, if desired
    if pos_x or pos_y:
        movVec = [pos_x, pos_y, 0]
        if verbose :
            print "new origin position =", movVec
            rs.ObjectColor(rs.AddCurve( [[0,0,0],movVec] ), [200,0,200]) 
        for i,panel in enumerate(panels):        panels[i]        = [ rs.VectorAdd(cor, movVec) for cor in panel ]
        for i,panel in enumerate(panels_sleeve): panels_sleeve[i] = [ rs.VectorAdd(cor, movVec) for cor in panel ]
        for i,panel in enumerate(panels_right):  panels_right[i]  = [ rs.VectorAdd(cor, movVec) for cor in panel ]
        for i,panel in enumerate(panels_left):   panels_left[i]   = [ rs.VectorAdd(cor, movVec) for cor in panel ]
        for i,section in enumerate(sections):    sections[i]      = [ rs.VectorAdd(cor, movVec) for cor in section ]
        cvs_sleeve = [rs.VectorAdd(crv, movVec) for crv in cvs_sleeve]
        cvs_right  = [rs.VectorAdd(crv, movVec) for crv in cvs_right]
        cvs_left   = [rs.VectorAdd(crv, movVec) for crv in cvs_left]
    ######## depict demo
    if demo :
        ######## the sections
        if 1 :
            for section in sections :
                rs.AddCurve( section, 1 )
                rs.Sleep( verbose/10 )
        ######## default panels
    else: # if not demo
        pass
        #rs.AddCurve( sections[0], 1 )
        #rs.AddCurve( sections[-1], 1 )
        #rs.ObjectColor(rs.AllObjects()[:2], [100,100,120])
        #rs.ObjectName( rs.AllObjects()[:2], "lodgeSections")
        #rs.ZoomExtents()

    return [ panels, panels_sleeve, panels_right, panels_left, sections, cvs_sleeve, cvs_right, cvs_left ]



################
######## functions 4giza & ra
################
##################
# list of pyramids
# list_name    "name"      height*)  OSM_ID         *)lt wiki
cheops    =  [ "cheops",    146.60, "4420397"] 
chefren   =  [ "chefren",   143.50, "4420396"]
mykerinos =  [ "mykerinos",  65.00, "4420398"]
henutsen   = [ "henutsen",   29.60, "219797726"]    # Princess: King's daughter: Said to be a daughter of Khufu on a stela placed in the temple during the 26th dynasty, but more likely to be a wife
meritetis  = [ "meritetis",  30.00, "219797724"]   # King's wife: Wife of Khufu
hetepheres = [ "hetepheres", 30.25, "198032492"]  # Queen HetepheresKing's wife, king's daughter: Wife of Sneferu and mother of Khufu 
khentkawes = [ "khentkawes", 18.50, "73174154"]  # Queen 
queen_A = [ "queen_A", 21.2, "25416060"]        # Queen A
queen_B = [ "queen_B", 21.2, "25416067"]       # Queen B
queen_C = [ "queen_C", 21.2, "25416093"]      # Queen C

#################################################
# collect all pyramid_list into one pyramids_list
pyramids = [ cheops, chefren, mykerinos
            ,henutsen, meritetis, hetepheres
            ,khentkawes
            ,queen_A, queen_B, queen_C
           ]
######## generate the pyramids @ giza / egypt / africa
def makePyramid (NameOrNumber=2, SrfsOrCrvs=0, verbose = 0):
    rs.CurrentLayer("Default")
    rs.LayerLocked ("OSM", 1)
    if verbose: rs.Redraw()
    for p, pyra in enumerate(pyramids):
        if type(NameOrNumber)==int and p==NameOrNumber:# or pyramids[i][0] == str(NameOrNumber):
            break
        if type(NameOrNumber)==str and pyra[0]==NameOrNumber:
            break
    print "pyramids["+str(p)+"][0]", pyramids[p][0]
    pyraNam    = pyramids[p][0]
    height     = pyramids[p][1]
    osmID      = pyramids[p][2]
    baseCrv = rs.ObjectsByName("_bldg3D_crv_"+osmID)
    if not baseCrv: baseCrv = rs.ObjectsByName("Wcrv_"+osmID)
    baseCrv = baseCrv[0]        # ObjectsByName() always returns list !
    baseCoords = rs.CurveEditPoints( baseCrv )[0:4]
    #dm.textDots( baseCoords )
    cen = pntCentroid( baseCoords )
    top = rs.VectorAdd( cen, [0, 0, height] )
    #rs.AddPoint( center )
    #rs.AddCurve( [center, top] )
    #########################################################
    # sort baseCoords radially - so that face_0 facing south:
    baseCoords = [ cor[1] for cor in sorted( [ [rs.Angle(cen, cor)[0], cor] for cor in baseCoords ] )]
    if len(pyramids[p]) < 5:
        pyramids.append( top )
        pyramids.append( baseCoords )
    else:
        pyramids[3]=top
        pyramids[4]=baseCoords
    #########################################################
    if 1:
        rs.CurrentLayer("GIZA::pyramids")
        if not rs.ObjectsByName(pyraNam+"_base"):
            rs.ObjectName(rs.AddCurve( baseCoords, 1), pyraNam+"_base" )
        for i in range(4):
            p0 = baseCoords[i]                      # some rocket science :
            p1 = baseCoords[i + 1 - 4*(i==3)]       # need last + first pnt of baseCoords
            if SrfsOrCrvs==0: obj = rs.AddSrfPt( [p0, p1, top] )  # surface - or..
            else: obj = rs.AddCurve( [p0, p1, top], 1 )         # .. just curves
            rs.ObjectName( obj, pyraNam+"_face_"+str(i) )# name it for later adressing
        pyra.append( top )                          # top coord appended to each single pyramid_list..
        pyra.append( baseCoords )                   # ..same with baseCoords 
        #### we respect the sad problems of rhino@mac:
        #rs.ZoomBoundingBox(rs.BoundingBox(rs.ObjectsByName("*_pyramid")))
        rs.CurrentLayer("Default")
        rs.LayerLocked ("OSM", 0)
        rs.DeleteObjects (rs.ObjectsByName("_bldg3D_higBy_hig_"+osmID))
        rs.HideObject (baseCrv)
    rs.CurrentLayer("Default")
    rs.LayerLocked ("OSM", 1)

######################### 4 pyramids mit_ohne OSM
########## action directe / 2020 12 15 / diag / sorry dafuer :)
rs.EnableRedraw(0)
pyras=[
["cheops", 146.6, 4420397, [1248.52044692,1177.13508851,146.6],
[[1119.11710484,1044.36437918,0.0],[1378.68076153,1045.09689965,0.0],[1377.91265704,1309.92508270,0.0],[1118.37126425,1309.15399252,0.0]]],
["chefren", 143.5, 4420396, [863.452413325,765.592578692,143.5],
[[739.094627173,642.396896715,0.0],[988.160855873,642.743869166,0.0],[987.804633503,888.788264014,0.0],[738.749536751,888.441284875,0.0]]],
["mykerinos", 65.0, 4420398, [592.158465300,320.622683574,65.0],
[[534.010729284,264.613746413,0.0],[650.128090131,264.420989649,0.0],[650.306201316,376.631619887,0.0],[534.188840470,376.824378347,0.0]]],
["henutsen", 29.6, 219797726, [1472.76242917,1010.17695143,29.6],
[[1446.28508828,984.182182777,0.0],[1498.62751285,983.578177545,0.0],[1499.23977005,1036.16529323,0.0],[1446.89734548,1036.78215218,0.0]]],
["meritetis", 30.0, 219797724, [1472.74294826,1069.51108927,30.0],
[[1446.34074803,1042.51380133,0.0],[1498.47166557,1041.87123964,0.0],[1499.13958251,1096.51480147,0.0],[1447.01979692,1097.14451462,0.0]]],
["hetepheres", 30.25, 198032492, [1471.22622020,1137.33379761,30.25],
[[1446.04018540,1111.85924919,0.0],[1496.12282431,1111.57652051,0.0],[1496.41225499,1162.80191979,0.0],[1446.32961608,1163.09750096,0.0]]],
["khentkawes", 18.5, 73174154, [1392.15876887,434.391845831,18.5],
[[1369.09615337,407.048940709,0.0],[1420.91537633,413.114422728,0.0],[1415.21581840,461.741187826,0.0],[1363.40772739,455.662832060,0.0]]],
["queen_A", 21.2, 25416060, [588.713127060,209.858105857,21.2],
[[570.234091589,190.903771808,0.0],[607.047447194,190.903771808,0.0],[607.047447194,228.812439906,0.0],[570.523522264,228.812439906,0.0]]],
["queen_B", 21.2, 25416067, [534.712042076,207.590007688,21.2],
[[519.650514972,191.212180857,0.0],[549.628853842,191.083677085,0.0],[549.762437231,223.967834353,0.0],[519.806362259,224.096338458,0.0]]],
["queen_C", 21.2, 25416093, [482.467022060,206.526636076,21.2],
[[468.554868698,191.263582365,0.0],[496.573984530,191.456338026,0.0],[496.373609447,221.783264813,0.0],[468.365625564,221.603359102,0.0]]]
]

#pyras = pyramids[:]
pyramids = pyras
#########################################
############### make pyramid mit_ohne OSM / 2020 12 15 / diag
def makePyramid (NameOrNumber=2, SrfsOrCrvs=0, verbose = 0):
    pyramids = pyras
    rs.UnitSystem(4) # meters
    if "GIZA::pyramids" not in rs.LayerNames():
        newEmptyLayer( "GIZA::pyramids", [240,230,100] )
    if verbose: rs.Redraw()
    for p, pyra in enumerate(pyramids):
        if type(NameOrNumber)==int and p==NameOrNumber:break# or pyramids[i][0] == str(NameOrNumber):
        if type(NameOrNumber)==str and pyra[0]==NameOrNumber:break
    if verbose: print "pyramids["+str(p)+"] :", pyramids[p][0]
    pyraNam     = pyramids[p][0]
    top         = pyramids[p][3]
    baseCoords  = pyramids[p][4]
    ############################

    rs.CurrentLayer("GIZA::pyramids")
    rs.DeleteObjects(rs.ObjectsByName(pyraNam+"_face_*"))
    rs.DeleteObjects(rs.ObjectsByName(pyraNam+"_base"))
    rs.ObjectName(rs.AddCurve( baseCoords, 1), pyraNam+"_base" )
    if verbose: rs.Redraw()
    for i in range(4):
        p0 = baseCoords[i]                      # some rocket science :
        p1 = baseCoords[i + 1 - 4*(i==3)]       # need last + first pnt of baseCoords
        if SrfsOrCrvs==0: obj = rs.AddSrfPt( [p0, p1, top] )  # surface - or..
        else: obj = rs.AddCurve( [p0, p1, top], 1 )           # .. just curves
        rs.ObjectName( obj, pyraNam+"_face_"+str(i) )         # name it for later adressing
        if verbose: rs.Redraw()
    if verbose: rs.Redraw()
    pyramids = pyras
    rs.CurrentLayer("Default")


################
######## functions for interaction
################

######## break if you need to
def esc(wo=""):
    if _host.EscapePressed(0):
        raise Exception(wo+'***ESCape*Key*Pressed***')
        #print rs.CommandHistory()


def TimeConsumingTask():
    scriptcontext = dm.scriptcontext    
    for i in range(10000):
        # Was escape key pressed?
        if (scriptcontext.escape_test(False)):
            print "TimeConsumingTask cancelled."
            break
        print i




################
######## text and display functions
################

######## adding leading zeros
def textPre0( val, sign="0", sollLen=1 ):
    # eng leading zero
    anz = sollLen-len(str(val))
    if anz>0:# and val < pow(10, anz):
        for i in range(anz): val = sign+str(val)
    return str(val)

######## adding leading zeros
def textDots(coordList, strX="", justName=0):
    state = rs.EnableRedraw(0)
    ids = []
    if len(coordList):
        for i, pt in enumerate(coordList):
            if not justName: 
                ids.append(rs.AddTextDot(str(i)+strX, pt))
            else:
                ids.append(rs.AddTextDot(strX, pt))
    else:
        print "***________no list > no textDots"
    rs.EnableRedraw(state)
    return ids




################
######## timing functions
################

######## timing variables

######## set time
def timeSet():
    global timeX
    timeX = time.time()

######## get time
def timeGet(verbose=0, prenull=0):
    '''
    timeGet(verbose=0, prenull=0)
    Parameters:
        verbose: int 0/1 print
        prenull: int, textPre0(tim, "0", prenull)
    Returns:
        round(tim,2)
    '''
    global timeX
    tim = float(time.time())
    tim -=  float(timeX)
    tim = round(tim,2)
    txt = str(tim)
    if prenull:
        txt = textPre0(tim, sign="0", sollLen=prenull)
        print "txt", txt
    if verbose:
        print str(txt)+"s"
    return round(tim,2)


######## transpose seconds to minutes
def timeSec2Min( secs ):
    min = int(secs/60.0)
    sec = int(secs-min*60)
    if min > 59:
        hor = int(min/60.0)
        min = int(min-hor*60)
        return textPre0(hor,1)+":"+textPre0(min,1)+":"+textPre0(sec,1)+" "
    min = textPre0(min,1)
    sec = textPre0(sec,1)
    return textPre0(min,1)+"min"+textPre0(sec,1)+"s"



################
######## date/time/location- and osm-related functions
################

def ortX(ortX):
    ort = "guinea"
    lat = 0#
    lon = 0#
    timeZone = 0#
    ### graz
    if ortX == "graz": 
        ort = "graz"
        lat = 47.0654 #=kronesgasse / opernring 47.07086780
        lon = 15.4486 #                         15.43827860
        timeZone = 1
    
    if ortX == "kollerweg":
        ort = "kollerweg"
        lat = 47.130380844984266 # lt google maps
        lon = 15.518638635469191 
        timeZone = 1
    ### NYC, UN headquaters
    if ortX == "NYC UN": 
        lat = 40.7492161
        lon = -73.96867250
        timeZone = -5
    ### stonehenge, UK
    if ortX == "stonehenge": 
        lat = 51.178889
        lon = -1.826111
        timeZone = 0
    ### gizeh, egypt
    if ortX == "giza": ### cairo 30 2 N .. 31 14 E
        ort == "giza"
        lat = 31.1241000
        lon = 29.9694000
        timeZone = 2
    if ortX == "koenigsberg": 
        ort == "koenigsberg"
        lat = 54.6969000
        lon = 20.4943000
        timeZone = 2
    if ortX == "shepperton": 
        ort == "shepperton"
        lat = 51.3928000
        lon = -0.4964000
        timeZone = 0
    if ortX == "barcelona": 
        ort == "barcelona"
        lat = 41.3737000
        lon = 2.1133000
        timeZone = 1
    return [lat, lon, timeZone]

def daysInMonth(year):
    return [31, 28+(int(year)%4==0), 31,30,31,30,31,31,30,31,30,31]

def number2date( number=31, year=2021):
    months = daysInMonth(year)
    for i in range(0, 11):
        if number <= months[i]:
            return [i+1, number]
        number -= months[i]
        if number <= months[i+1]:
            return [i+2, number]
#print number2date( number=32+28, year=2020)
def date2number( year=2020, mon=12, day=30, verbose=0 ):
    months = daysInMonth(year)
    if day > daysInMonth(year)[int(mon)-1]:
        if verbose: print "*** ________no good date: date2number("+str(year)+(", ")+str(mon)+", "+str(day)+") | return =",
        return 0
    number = 0
    for m in range(1, int(mon)): number += months[m-1]
    return number+int(day)
#print date2number(year=2020, mon=2, day=31)
# diag 20 12 18 21:00:
def date2number( year=2020, mon=12, day=30, verbose=0 ):
    if type(year)==list:
        return date2number( year[0], year[1], year[2], verbose=0 )
    months = daysInMonth(year)
    if day > daysInMonth(year)[int(mon)-1]:
        if verbose: print "*** ________no good date: date2number("+str(year)+(", ")+str(mon)+", "+str(day)+") | return =",
        return 0
    number = 0
    for m in range(1, int(mon)): number += months[m-1]
    return number+int(day)
#print date2number(year=2020, mon=2, day=31)

def getDateNow():
    now = datetime.now()
    #print "day :", type(now.day), now.day
    return [now.year, now.month, now.day, now.hour, now.minute, now.second]

_2day = getDateNow()
dayNumber2day = date2number(_2day[0], _2day[1], _2day[2])


dlSaving=0 # sommerzeit o.a.
dat = getDateNow()
ort = "graz" # default
ort = "giza" # default
#ort = "koenigsberg" # default
#ort = "shepperton" # default

def setSun ( year=dat[0], mon=dat[1], day=dat[2], hour=dat[3], min=dat[4], sec=dat[5], verbose=0):
    sun = sc.doc.Lights.Sun
    sun.Enabled = 1
    sun.TimeZone = ortX(ort)[2]
    sun.DaylightSavingMinutes = dlSaving
    if day <= daysInMonth(year)[mon-1]:
        sun.SetPosition(System.DateTime(year, mon, day, hour, min, sec), ortX(ort)[0],ortX(ort)[1])
        if verbose:
            print "    setSun @", year, textPre0(mon, sign="0", sollLen=2), textPre0(day, sign="0", sollLen=2), "(day# "+str(date2number(year,mon,day))+")","@", str(hour)+":"+str(min)+":"+str(sec)+" @ "+ort.upper()+" @ lat/lon =",str(round(ortX(ort)[0],4))+"°/"+str(round(ortX(ort)[1],4))+"°", "| tZ = "+str(ortX(ort)[2]),
            print "| azi/alti =", str(round(sun.Azimuth,4))+"°/"+str(round(sun.Altitude,4))+"°\n"#,":"
            #print "*** [ azimut", round(sun.Azimuth,2), " / altitude",round(sun.Altitude,2),"/ sunVector [", sun.Vector, "] ]"
        #return [ sun.Azimuth, sun.Altitude, [sun.Vector[0], sun.Vector[1], sun.Vector[2]] ]
        sunVec = sun.Vector
        return [ sun.Azimuth, sun.Altitude, sun.Vector ]
    else:
        print "*** ________no good date: setSun("+str(year)+(", ")+str(mon)+", "+str(day)+") | return =",
        return [0,0,[0,0,0]]



################
######## delete functions
################

######## delete selected
def eS() :
    rs.DeleteObjects( rs.SelectedObjects() )

######## delete visible
def eV() :
    rs.SelectObjects( rs.AllObjects() )
    eS()

######## erase all
def eA() :
    rs.DeleteObjects( rs.AllObjects() )
    rs.Redraw()

######## delete AbsolutelyAll
def eAA():
    """Deletes Absolutely All objects, incl hidden and locked
    """
    rs.UnlockObjects(rs.AllObjects())
    rs.ShowObjects  (rs.AllObjects())
    eA()
    rs.Redraw()
    #print ("''' all objects deleted")

def eDup(verbose=1):
    selected = rs.SelectedObjects()
    rs.UnselectAllObjects()
    vorher = len(rs.AllObjects())
    rs.Command("_selDup", 0)
    if selected and rs.SelectedObjects():
        for obj in rs.SelectedObjects():
            if obj in selected: selected.remove( obj )
    rs.DeleteObjects( rs.SelectedObjects() )
    if selected: rs.SelectObjects(selected)
    if verbose: print "*** dm.eDup() deleted", vorher - len(rs.AllObjects()), "objects ***\n"
#eDup()
################
######## display functions
################

######## zoom all
def zA( proz=0.95 ) :
    rs.ZoomExtents( all=1 )
    rs.Command("-_zoom Factor "+str(proz)+" _enter", 0) ### diag 2021 11 07
    rs.Redraw()

######## zoom selected
def zS() :
    rs.ZoomSelected( all=1 )

def printDisplay(state=1):
    if state: rs.Command("_PrintDisplay State=On Color=Display Thickness=40 _enter", 0)
    else: rs.Command("_PrintDisplay State=Off _enter", 0)




################
######## math and array functions
################

######## very simple remapping of a value
def reMap(i, iMin, iMax, oMin, oMax):
    '''

    '''
    inFac  = float(i-iMin)/float(iMax-iMin)# 
    outFac = (oMax-oMin)
    return (oMin + outFac*inFac)

######## list mixxing  / diag
def permutate( list, num):
    return [ perm for perm in permutations(list, num) ]
    
def combinate( list, num):
    return [ comb for comb in combinations(list, num) ]


################
######## basic point and vector functions
################

######## get a point between two points
def pntInbetween( coord0, coord1, fac=.5 ) :
    vec = rs.VectorSubtract( coord1, coord0 )
    vec = rs.VectorScale( vec, fac )
    coord = rs.VectorAdd( coord0, vec )
    return coord

def normVec3pnts( p0, p1,p2):
    return rs.VectorUnitize(rs.VectorCrossProduct( rs.VectorSubtract(p0,p1), rs.VectorSubtract(p0,p2) ))

### _diag 2021 11 13
######## get segmets+1 points between two points
######## similar 2 rs.DivideCurve(..)
def pntsInbetween( coord0, coord1, segments, create_points=False, create_line=False):
    '''
    return = coords
    '''
    coords = []
    vec = rs.VectorSubtract( coord1, coord0 )
    for i in range(segments+1):
        coords.append(rs.VectorAdd( coord0, rs.VectorScale(vec, float(i)/segments) ) )
        print i/segments
    if create_points: rs.AddPoints( coords )
    if create_line  : rs.AddLine( coord1, coord0 )
    print len(coords)
    return coords



def circumcircleOfTriangle(p0, p1, p2, verbose = 1):
    lineA = symmetryLine(p0, p1)
    lineB = symmetryLine(p1, p2)
    center= rs.LineLineIntersection(lineA, lineB)[0]
    if verbose:
        rs.AddCircle(center, rs.Distance(center,p0) )
    return [center, rs.Distance(center,p0)]

######## get a point in center/centroid/mitte von coords/points/pointcloud
def pntCentroid( coords ) :
    x_sum = 0
    y_sum = 0
    z_sum = 0
    for coord in coords :
        x_sum += coord[ 0 ]
        y_sum += coord[ 1 ]
        z_sum += coord[ 2 ]
    num = len( coords )
    x = x_sum / num
    y = y_sum / num
    z = z_sum / num
    coord = [ x, y, z ]
    return coord

######## sort a li^st of coordinates depending on their distance to one point
def pntsSortDistancePnt( origin, coords ) :
    dists = []
    for coord in coords :
        dist = rs.Distance( origin, coord )
        dists.append( abs(dist) )
    zipped_lists = zip( *sorted( zip(dists,coords) ) )
    return list(zipped_lists[1])

######## sort a list of coordinates depending on their distance to one plane
def pntsSortDistancePln( plane, coords ) :
    dists = []
    for coord in coords :
        dist = rs.DistanceToPlane( plane, coord )
        dists.append( abs(dist) )
    zipped_lists = zip( *sorted( zip(dists,coords) ) )
    return zipped_lists[1]



################
######## point and vector functions for curves
################

######## perpendicular frame with normalized parameters
### ?? fehler diag 2021 11 21 ??
def plnCurvePerp( crv, crv_parameter=0.5 ) :
    ###crv_parameter = rs.CurveParameter( crv, crv_parameter ) 
    pln = rs.CurvePerpFrame( crv, crv_parameter )
    return pln

######## perpendicular distance ("normalabstand") point..curve
def pntCurveDist( pnt, crv  ):
    # para = rs.CurveClosestPoint(crv, pnt)
    # poin = rs.EvaluateCurve(crv, param)
    # dist = rs.Distance(pnt, poin)
    return rs.Distance(pnt, rs.EvaluateCurve(crv, rs.CurveClosestPoint(crv, pnt)))

######## perpendicular distance ("normalabstand") point..Line / _diag 2021 11 04
def pntLineDist( pnt, line):
    '''
    arguments:  pnt: [x,y,z]
               line: [point, point] 
       return: distance of nearest point on LINE from tst_pnt
    '''
    #line = [point0, point1]
    #poin = rs.LineClosestPoint(line, pnt)
    #dist = rs.Distance(pnt, poin)
    return rs.Distance(pnt, rs.LineClosestPoint(line, pnt))

######## perpendicular distance ("normalabstand") point..curve
def pntCurveClosest( pnt, crv  ):
    '''
    arguments: pnt: [x,y,z]
               crv: ID of curve
       return: pnt [x,y,z] on curve that is closest to pnt
    '''
    para = rs.CurveClosestPoint(crv, pnt)
    return rs.EvaluateCurve( crv, rs.CurveClosestPoint(crv, pnt) )

######## origin of the perpendicular frame
def pntCurvePerp( crv, crv_parameter=0.5 ) :
    prepframe = plnCurvePerp( crv, crv_parameter )
    pnt = prepframe[0]
    return pnt

######## axis of the perpendicular frame
def vecCurvePerpX( crv, crv_parameter=0.5 ) :
    prepframe = plnCurvePerp( crv, crv_parameter )
    vec = rs.VectorUnitize( prepframe[1] )
    return vec
def vecCurvePerpY( crv, crv_parameter=0.5 ) :
    prepframe = plnCurvePerp( crv, crv_parameter )
    vec = rs.VectorUnitize( prepframe[2] )
    return vec
def vecCurvePerpZ( crv, crv_parameter=0.5 ) :
    prepframe = plnCurvePerp( crv, crv_parameter )
    vec = rs.VectorUnitize( prepframe[3] )
    return vec

######## projection of the curve direction onto a plane
def vecCurvePerpNormal( crv, crv_parameter=0.5, nor=[pi,pi,pi] ) :
    prepframe = plnCurvePerp( crv, crv_parameter )
    dir = rs.VectorUnitize( prepframe[3] )
    vec = rs.VectorCrossProduct( dir, nor )
    ### vec = rs.VectorRotate( vec, 90, nor ) ### korr diag 2022 11 04
    vec = rs.VectorUnitize( vec )
    return vec

######## projections of the curve direction onto default planes
def vecCurvePerpXY( crv, crv_parameter=0.5 ) :
    vec = vecCurvePerpNormal( crv, crv_parameter, [0,0,1] )
    return vec
def vecCurvePerpXZ( crv, crv_parameter=0.5 ) :
    vec = vecCurvePerpNormal( crv, crv_parameter, [0,1,0] )
    return vec
def vecCurvePerpYZ( crv, crv_parameter=0.5 ) :
    vec = vecCurvePerpNormal( crv, crv_parameter, [1,0,0] )
    return vec

### new 2020 12 15 / diag
def pnt2str( pnt ): # [x,y,z] > "x,y,z" | useful at rs.command(..)
    return str(pnt[0])+","+str(pnt[1])+","+str(pnt[2])

def pnt2XY0( pnt ): # [x,y,z] > [x,y,0]
    return [pnt[0], pnt[1], 0]

def pnt2cor( pnt ): # [<Rhino.Geometry.Point3d object at 0x0000000000000104 [x,y,z]>] or 'vector' etc > [x,y,z]
    return [ val for val in pnt ]

def pnts2cors( pnts ): # [<Rhino.Geometry.Point3d object at 0x0000000000000104 [x,y,z]>] or 'vector' etc > [x,y,z]
    return [ [pnt[0],pnt[1],pnt[2]] for pnt in pnts]


#########################
### new 2022 11 29 / diag

def pnts2XY0( coords ):
    return [ pnt2XY0(cor) for cor in coords]

#########################
### new 2022 12 08 / diag
coords=[]
def pntRemoveByDist(coords=coords, minDist=1.0, randomize=1, verbose=1):
    lenVorher = len(coords)
    for i in range(1001):
        if randomize:
            random.shuffle(coords)
        distList = sorted( [ [pair, rs.Distance(pair[0],pair[1])] for pair in combinate( coords, 2) ], key=lambda sortKey: (sortKey[1]) )
        if verbose: print i, "len(coords)", len(coords), "min ",distList[0][1]
        if distList[0][1] < minDist:
            coords.remove(distList[0][0][0] )
            #rs.AddCurve( distList[0][0] )
            #rs.AddPoint( distList[0][0][0] )
        else:
            print "XXX pntRemoveByDist done ",
            if 1 or verbose: print ": cnt =", i,  "len(coords) vorher/nachher "+str(lenVorher)+"/"+str(len(coords))+" - minDist", distList[0][1], "\nXXXXXXXXXXXXXXXXXXXXXXXX"
            break
    return coords

'''
USAge
anz=100
a=10
coords = [ [random.uniform(0,a),random.uniform(0,a),0] for i in range(anz) ]
#pnts = rs.AddPoints( coords )
coords = pntRemoveByDist(coords=coords, minDist=0.5, verbose=0)
'''
    
##########
# ConvHull
# worx @ xy_plane only
def pntConvHull(coordList, verbose=0):
    avg=pntCentroid(coordList)
    start = a = min(coordList)
    coords = [start]
    for i in range(1,10001):
        esc()
        o = a
        a = avg
        for b in coordList:
            if (a[0]-o[0])*(b[1]-o[1])-(a[1]-o[1])*(b[0]-o[0]) < 0:
                a = b
        coords.append(a)
        if a == start:
            break
    if a == start:# or i == 1000:
        if verbose: print "*** convHull: i=",i,": len convCoords=", len(coords), "(from "+str(len(coordList))+"coords)"

    return coords

### new 2022 12 09 ff / diag
#########################
def pntRandVec (a=-1, b=1, anzahl=1):
    return  [[random.uniform(a,b) for i in range(3) ] for j in range(anzahl)]

def pntRandCoords (a=-1, b=1, anzahl=100):
    return  [[random.uniform(a,b) for i in range(3) ] for j in range(anzahl)]

def getSurfacePoints( objID ): 
    rs.UnselectAllObjects()
    rs.Command("_solidPtOn _selID "+str(objID)+" _enter _enter", 0)
    coords = rs.ObjectGripLocations( objID )
    rs.Command("_pointsOff _enter", 0)
    return coords ### danke, andi !

################
######## layer and material functions
################

######## set up an empty new layer in rhino
def newEmptyLayer( lay, color=[127,127,127], parent="", transe=0.0, gloss=0.0, refl=0.0 ):
    #rs.CurrentLayer( "Default")
    layX = lay
    if parent: layX = parent+"::"+lay
    rs.AddLayer( lay, color, parent=parent )
    if rs.IsLayer( layX ) :
        rs.ShowObjects( rs.ObjectsByLayer( layX ) )
        rs.DeleteObjects( rs.ObjectsByLayer( layX ) )
        #rs.PurgeLayer( layX )
    rs.LayerColor(layX, color)
    rgbMat2Lay( layX, *color, T=transe, Gloss=gloss, Refl=refl, matNam=layX+"_layMat" )
    rs.Redraw()
    rs.CurrentLayer( lay )

######## assign a material to an object 
def rgbMat2Obj(obj_id = "", R=127, G=127, B=127, T=0.0, Gloss=0, Refl=0.0, matNam="none"):
    """ Adds Material to Objects.
    Parameters:
      obj_id = Guid of Geometry in RhinoDoc
      Red[opt] = 0-255
      Green[opt] = 0-255
      Blue[opt] = 0-255
      Transparency[opt] = 0.0 - 1.0, 1.0 being transparent
      Glossiness[opt] = 0.0 - 255.0, 255.0 being glossy
      Reflectivity[opt] = 0.0 - 1.0, 1.0 being fully reflectant
      matName[opt] = Materialname as String
    Returns:
      Material index
    """
    if obj_id: index = rs.AddMaterialToObject(obj_id)
    else: index = sc.doc.Materials.Add()
    R=int(R)
    G=int(G)
    B=int(B)
    rs.MaterialColor( index, (R, G, B) )
    rs.MaterialTransparency( index, T-0.000001 ) # T 0.0..1.0
    rs.MaterialShine( index, Gloss*255 )         # Gloss 0.0..1.0 // diag 2022 01 28
    #rs.MaterialReflectiveColor( index, (191, 191, 255) )
    newMat = sc.doc.Materials[index]
    newMat.Reflectivity=Refl
    sc.doc.Materials.Modify(newMat,index,1)
    if matNam == "none": matNam = "mat_%s_%s_%s_%s_ind_%s" % (R, G, B, int(T*100), index)
    #print "***", matNam#, #" .. created (ind", index, ")"
    rs.MaterialName (index, matNam)
    return index

######## assign a material to a layer 
def rgbMat2Lay(lay_nam = "", R=127, G=127, B=127, T=0.0, Gloss=0.0, Refl=0.0, matNam="none"):
    """ Adds Material to LAYER.
    Parameters:
      lay_nam = layerName
      Red[opt] = 0-255
      Green[opt] = 0-255
      Blue[opt] = 0-255
      Transparency[opt] = 0.0 - 1.0, 1.0 being transparent
      Glossiness[opt] = 0.0 - 1.0, 1.0 being glossy
      Reflectivity[opt] = 0.0 - 1.0, 1.0 being fully reflectant
      matName[opt] = Materialname as String
    Returns:
      Material index
    """
    if lay_nam: index = rs.AddMaterialToLayer(lay_nam)
    else: index = sc.doc.Materials.Add()
    rs.ResetMaterial(index)
    R=int(R)
    G=int(G)
    B=int(B)
    #rs.MaterialReflectiveColor( index, (191, 191, 255) )
    newMat = sc.doc.Materials[index]
    newMat.Reflectivity=Refl
    sc.doc.Materials.Modify(newMat, index, 1)
    if matNam == "none": matNam = "mat_%s_%s_%s_%s_ind_%s" % (R, G, B, int(T*100), index)
    #print "***", matNam#, #" .. created (ind", index, ")"
    rs.MaterialColor( index, (R, G, B) )
    rs.MaterialTransparency( index, T-0.000001 ) # T 0.0..1.0
    rs.MaterialShine( index, Gloss*255 )             # Gloss 0.0..1.0
    rs.MaterialName (index, matNam)
    return index

### experimantal
def PointRadius(displayModeX=0, rad=3,  verbose=0 ):
    '''
        arguments:
                displayModeX: int .. number of displayMode
                rad = 3 : point radius
                verbose : quasselig
        Standard displayModes_numbers
        0 Wireframe
        1 Shaded
        2 Rendered
        3 Ghosted
        4 XRay
        5 Technical
        6 Artistic
        7 Pen
        8 Arctic
        9 Raytraced
        10 VRayInteractive 
    '''
    display_modes = Rhino.Display.DisplayModeDescription.GetDisplayModes()
    #point_styles = Rhino.Display.DisplayModeDescription.PointStyles()
    for i,mod in enumerate(display_modes): 
        #print mod.EnglishName
        if mod.EnglishName == displayModeX:
            selected_description =  display_modes[i]
            #print selected_description
    ###DISPLAY_MODE##
    if isinstance(displayModeX, int) and displayModeX < 11:
        selected_description = display_modes[displayModeX]
    '''
    point styles
    1 square
    3
    '''
    ###RADIUS POINTS###
    radi = rad
    disX = displayModeX
    for i, disX in enumerate(display_modes):
        english_name = disX.EnglishName
        english_name = english_name.translate(None, "_ -,.")
        if verbose: print ""+str(i)+"..",str(english_name)[0:10]
        #print selected_description.DisplayAttributes.PointStyle
        #print selected_description.DisplayAttributes.PointRadius
    changes=range(displayModeX,displayModeX+1)
    if displayModeX>11: changes=range(11)
    for displayModeX in changes:
        selected_description = display_modes[displayModeX]
        selected_description.DisplayAttributes.PointRadius =  rad
        echo = Rhino.Display.DisplayModeDescription.UpdateDisplayMode(selected_description)
    if verbose and echo: print "Point Radius wurde auf", radi, "geaendert, danke Eszter!"
#PointRadius(displayModeX=13, rad=1,  verbose=1 )

def UNcutTool(dist=50, offs = 3, deleteIt = 1):
    if dist < 10: dist = 100
    currView = rs.ViewDisplayMode()
    rs.ViewDisplayMode(view=None, mode="wireframe")
    p0 = uno0
    p1 = rs.VectorAdd( uno0, rs.VectorScale(unoY,lenY) )
    p2 = rs.VectorAdd( p1, rs.VectorScale(unoX, lenX) )
    p3 = rs.VectorAdd( p0, rs.VectorScale(unoX, lenX) )
    center = pntInbetween(p0, p2)
    crv0 = rs.AddCurve( [p0, p1, p2, p3, p0], 1 )
    crv1 = rs.OffsetCurve(crv0, [0,0,0], dist)
    rs.MoveObject(crv1, [0,0,-dist] )
    aussen = rs.ExtrudeCurveStraight( crv1, [0,0,0], [0,0,lenZ+dist*2] )
    rs.CapPlanarHoles(aussen)
    #rs.DeleteObjects( [crv0, crv1] )
    
    p0 = rs.VectorAdd( uno0, rs.VectorScale(unoX, lenX+dist*-3) )
    p1 = rs.VectorAdd( p0, rs.VectorScale(unoY,lenY) )
    p2 = rs.VectorAdd( p1, rs.VectorScale(unoX, lenX+dist*4) )
    p3 = rs.VectorAdd( p0, rs.VectorScale(unoX, lenX+dist*4) )
    crv0 = rs.AddCurve( [p0, p1, p2, p3, p0], 1)
    crv1 = rs.OffsetCurve(crv0, uno0, 20)
    crv1o = rs.OffsetCurve(crv1, [0,0,0], 20+offs)
    rs.MoveObject(crv1o, [0,0,-offs])
    innen = rs.ExtrudeCurveStraight( crv1o, [0,0,0], [0,0,lenZ+2*offs] )
    rs.CapPlanarHoles(innen)
    #rs.DeleteObjects( [crv0,crv1, crv1o] )
    cutBody = rs.BooleanDifference(aussen, innen)
    rs.DeleteObjects( [crv0,crv1, innen, aussen] ) 
    rs.UnselectAllObjects()
    rs.SelectObjects(rs.AllObjects())
    rs.UnselectObject(cutBody)
    rs.Command("_split _selID "+str(cutBody)+" _enter _enter",0)
    rs.UnselectAllObjects()
    rs.SelectObject(cutBody)
    rs.Command("_SelVolumeObject _enter", 0)
    #rs.DeleteObject(cutBody)
    anz = len(rs.SelectedObjects())
    rs.Redraw()
    rs.ZoomSelected()
    rs.Sleep(1000)
    print "*** UNcutTool split", anz,"objects",
    if deleteIt:
        rs.DeleteObjects( rs.SelectedObjects())
        print ".. und weg sind sie !"
    rs.ViewDisplayMode(view=None, mode=currView)



def getPixels(imagePath, steps_x=5, steps_y=5):
    # https://discourse.mcneel.com/t/how-to-store-list-of-list-when-reading-rgb-values-of-an-image/88276/6
    """
    Gets the pixel colors of an image.
    Args:
      image_path: An absolute path to an image file.
      steps_x: A number of pixels to sample in image x-direction.
      steps_y: A number of pixels to sample in image y-direction.
    Returns:
      A list of lists with a sublist of pixel colors 
        for each sampled row of the image .. starting linx_unten
    """
    ###
    print imagePath
    ###
    bitmap = System.Drawing.Bitmap.FromFile(imagePath)
    print "wid", bitmap.Width, "steps_x", steps_x, ">>", int(bitmap.Width/steps_x)
    print "hig", bitmap.Height, "steps_y", steps_y, ">>", int(bitmap.Height/steps_y)
    pixels = []
    for y in xrange(bitmap.Height-1,-1,-1):
        if y % steps_y == 0:
            row_pixels = []
            for x in xrange(bitmap.Width):
                if x % steps_x == 0:
                    pixel = bitmap.GetPixel(x, y)
                    col = [pixel[0],  pixel[1],  pixel[2] ]
                    row_pixels.append(col)
            pixels.append(row_pixels)
    return pixels
    



############################
### DM2_w22 demo UN_NYC_slab / simplyfied version
unoX = [ 0.876922678230265,-0.480631476711064,0 ]
unoY = [ 0.480631476711061,0.876922678230267,0  ]
uno0 = [ 727.495136230253,531.705385598354,0.00 ]

slabs = L = 11
depth = D = 4
floors= H = 40
fH = 3.9
lenY = 116.56
lenX =  28.18
lenZ = 156.00

def getUnoGridCoords(L=11, D=4, H=40, fH = 4.0):
    #baseCoords = rs.CurveEditPoints(rs.ObjectsByName("_bldg3D_crv_137989966")[0])
    #baseXYZ = baseCoords[14]
    #baseXYZ = [727.495136230253,531.705385598354,0.00]
    #print baseXYZ
    #    dirY = rs.VectorSubtract( baseCoords[10], baseCoords[14] )
    #    dirX = [dirY[1], dirY[0]*-1.0,0] # normal vec
    #    lenY = rs.VectorLength( dirY ) 
    #    lenX = rs.VectorLength( rs.VectorSubtract( baseCoords[15], baseCoords[14] ) )
    #    uniX = rs.VectorUnitize(dirX)
    #    uniY = rs.VectorUnitize(dirY)
    #    uniX = rs.VectorUnitize(dirX)
    #    uniY = rs.VectorUnitize(dirY)
    D += D<2
    slab_spacing = lenY/(L-1)
    UnoGridCoords = []
    for z in range(H):
        for x in range(D):
            for y in range(L):
                cor = uno0
                cor = rs.VectorAdd( cor, rs.VectorScale(unoX, lenX*x/(D-1)))
                cor = rs.VectorAdd( cor, rs.VectorScale(unoY, slab_spacing*y))
                cor = rs.VectorAdd( cor, [0,0, fH*z])
                UnoGridCoords.append( cor )
    print "*** UnoGridCoords = [",L,"*",D,"*", H,"=", len(UnoGridCoords),"coords]   by getUnoGridCoords() @ DM2_lib ***"
    print "**************************************************************************************\n"
    return UnoGridCoords


def UN_slab(showAll=0):
    #eA()
    slabs = 11
    depth = 4
    floors= 40
    fH = 3.9
    lenY = 116.56
    lenX =  28.18
    lenZ = 156.00
    rs.ObjectColor( rs.AddLine( uno0, rs.VectorAdd( uno0, rs.VectorScale(unoX, lenX) ) ), [200,0,0] )# x/Red
    rs.ObjectColor( rs.AddLine( uno0, rs.VectorAdd( uno0, rs.VectorScale(unoY, lenY) ) ), [0,200,0] )# y/Green
    rs.ObjectColor( rs.AddLine( uno0, rs.VectorAdd( uno0, [0,0,40*fH] ) ),                [0,0,200] )# z/Blue
    
    linX = rs.AllObjects()[2]
    linY = rs.AllObjects()[1]
    linZ = rs.AllObjects()[0]
    if showAll:
        for y in range(0, slabs+1):
            rs.CopyObject( linZ, rs.VectorScale( unoY, lenY/slabs*y ) )
            for x in range(1, depth+1):
                rs.CopyObject( rs.AllObjects()[0], rs.VectorScale( unoX, lenX/depth) )
        rs.CopyObject( linY, rs.VectorScale( unoX, lenX) )
        for f in range(1, floors+1):
            rs.CopyObject( linY, [0,0,fH*f] )
        rs.CopyObject( rs.AllObjects()[0], rs.VectorScale( unoX, lenX) )
    
        for y in range(0, slabs+1):
            pass
            rs.CopyObject( linX, rs.VectorScale( unoY, lenY/slabs*y ) )
            rs.CopyObject( rs.AllObjects()[0], [0,0,fH*floors] )
        eDup()
    zA()
### UN_slab(showAll=1)
### DM2_w22 demo UN_NYC_slab
############################

################
######## basic library data and functions
################


def echo( on ):
        Rhino.ApplicationSettings.AppearanceSettings.EchoCommandsToHistoryWindow = on
        Rhino.ApplicationSettings.AppearanceSettings.EchoPromptsToHistoryWindow = on

echo(1)

#rs.EnableRedraw(0)
Rhino.ApplicationSettings.AppearanceSettings.EchoCommandsToHistoryWindow = 1
Rhino.ApplicationSettings.AppearanceSettings.EchoPromptsToHistoryWindow  = 1



######## feedback in status bar
print lib_cc
print "    sourced", datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~"

#ort = "giza" # default
ort = "graz" # default
#ort = "shepperton" # default
#ort = "NYC UN" # default

schlafTage = date2number(2022,12,24) - dayNumber2day
print "btw : "+str(schlafTage)+" tage sinds noch bis weihnachten - dh, eh noch",schlafTage*4, "stunden schlaf :)"
#print "      frohes schaffen wuenschen _diag und fabian !\n"
setSun(verbose=1)

#print "changes___:"
#print "2021 11 29: exp PointRadius(displayMode=0, rad=3,  verbose=0 )"
#print "2021 11 25: neu eDup() - delete duplicates / chg 2021 11 30"
#print "2021 11 20: kor plnCurvePerp()"
#print "          : neu getSurfacePoints(polySrf)"
#print "          : neu zA( proz=0.95 ) - zoom extents + % smaller"
#print "          : neu printDisplay( 1/0 )"
########################
rs.EnableRedraw(1)

#coords = []
#print "***len dm.coords =", len(coords),"\n*******************************"

allLodgeData  = getLodge( demo=0, verbose=0 )
UnoGridCoords = getUnoGridCoords(11, 4, 40, 4.0)
p0 = getUnoCoord( 0,   0,   0)
p1 = getUnoCoord( L-1, 0,   0)
p2 = getUnoCoord( L-1, D-1, 0)
p3 = getUnoCoord( 0,   D-1, 0)
UnoBaseCoords = [p0, p1, p2, p3, p0]
### just for PULA demo w22 ue:08
allBuildingsCoords = []