#######################
#-*- coding: utf-8 -*-


######## DM2
######## iam@richdank.com and agruber@tugraz.at
######## http://iam.tugraz.at/dm2/
######## library



################
######## basic library data and functions
################

######## credits
lib_cc = "DM2 LIBRARY ~~~~~~~~~~~~~~~~~~~"
lib_cc += "\n    2022-01-09 12:00:00 version ### w21_08"
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
pi = math.pi

################
######## 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 "*** getTetraCoords(..):", 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 getUnoGridCoords(L=11, D=4, H=40, fH = 4.0):
    baseCoords = rs.CurveEditPoints(rs.ObjectsByName("_bldg3D_crv_137989966")[0])
    baseXYZ = baseCoords[14]
    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)
    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 = baseXYZ
                cor = rs.VectorAdd( cor, rs.VectorScale(uniX, lenX*x/(D-1)))
                cor = rs.VectorAdd( cor, rs.VectorScale(uniY, slab_spacing*y))
                cor = rs.VectorAdd( cor, [0,0, fH*z])
                UnoGridCoords.append( cor )
    print "*** len UnoGridCoords =",L,"*",D,"*", H,"=", len(UnoGridCoords),"   by getUnoGridCoords() @ dm2_lib"
    return UnoGridCoords

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=1 ) :
    """
    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 1 or verbose:
        print "                      ________"
        print "                     /_/_/_|__\\"
        print "### myLODGE          |_|_|_|__|\n############################### all data returned 2 allData[]\n"

    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
    section0_id = rs.AddCurve( section0, 1 );
    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
        if 0 :
            for panel in panels :
                rs.AddCurve( panel, 1 )
                #new_order = [ panel[2],panel[0],panel[3],panel[1] ]
                #rs.ObjectColor(rs.AddCurve( new_order, 2 ), [100,100,120])
                #rs.Sleep( verbose/10 )
        ######## the cvs
        if 0 :
            rs.AddPoints( cvs_sleeve )
            rs.AddPoints( cvs_right )
            rs.AddPoints( cvs_left )
    else: # if not demo
        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]
    #dm2.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( ):
    if _host.EscapePressed(0): raise Exception('***ESCape*Key*Pressed***')



################
######## 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
global timeX
timeX = time.time()

######## 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)
    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() )

######## delete AbsolutelyAll
def eAA():
    """Deletes Absolutely All objects, incl hidden and locked
    """
    rs.UnlockObjects(rs.AllObjects())
    rs.ShowObjects  (rs.AllObjects())
    eA()
    #print ("''' all objects deleted")

def eDup():
    rs.UnselectAllObjects()
    rs.Command("selDup delete enter", 0)

################
######## 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

######## 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 list 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 )
    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 2021 01 18 / diag

def pnts2XY0( coords ):
    return [ pnt2XY0(cor) for cor in coords]

##########
# ConvHull
# worx @ xy_plane only
def pntConvHull(coordList, verbose=0):
    avg=pntCentroid(coordList)
    start = a = min(coordList)
    coords = [start]
    for i in range(1,1000001):
        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 or i == 1000000:
            if verbose: print "*** convHull:", len(coords), "(from "+str(len(coordList))+") coords"
            break
    return coords

### new 2021 11 09 ff / diag
#########################
def pntRandVec (a=-1, b=1):
    return  [random.uniform(a,b) for i in range(3) ]

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 )             # Gloss 0.0..255.0
    #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(displayMode=0, rad=3,  verbose=1 ):
    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 == displayMode:
            selected_description =  display_modes[i]
            #print selected_description
    ###DISPLAY_MODE##
    if isinstance(displayMode, int):
        selected_description = display_modes[displayMode]
                                            #Standard
                                            #0 Wireframe
                                            #1 Shaded
                                            #2 Rendered
                                            #3 Ghosted
                                            #4 XRay
                                            #5 Technical
                                            #6 Artistic
                                            #7 Pen
                                            #8 Arctic
                                            #9 Raytraced
                                            #10 VRayInteractive 
    '''
    point styles
    1 square
    3
    '''
    ###RADIUS POINTS###
    radi = rad
    for i, displayModeX in enumerate(display_modes):
        english_name = displayModeX.EnglishName
        english_name = english_name.translate(None, "_ -,.")
        if verbose: print i, english_name
    #print selected_description.DisplayAttributes.PointStyle
    #print selected_description.DisplayAttributes.PointRadius
    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!"


##########################
### DM2_w21 johanna theurl 
nearest_centers_links=[]
nearest_IDs_links=[]
nearest_centers_rechts=[]
nearest_IDs_rechts=[]

def getNearestAuflagers( bereich = 100.0):
    rs.CurrentView("Top")
    #bereich = 100.0
    offs_0 = rs.OffsetCurve(midCrv, [0,0,0], bereich)
    offs_1 = rs.OffsetCurve(midCrv, [0,0,0], -bereich)
    
    midCrv_start = rs.CurveStartPoint( midCrv )
    midCrv_end = rs.CurveEndPoint( midCrv )
    offs_0_start = rs.CurveStartPoint( offs_0 )
    offs_0_end = rs.CurveEndPoint( offs_0 )
    offs_1_start = rs.CurveStartPoint( offs_1 )
    offs_1_end = rs.CurveEndPoint( offs_1 )
    
    crv_0 = rs.AddLine( midCrv_start, offs_0_start )
    crv_1 = rs.AddLine( midCrv_end, offs_0_end )
    crv_2 = rs.AddLine( midCrv_start, offs_1_start )
    crv_3 = rs.AddLine( midCrv_end, offs_1_end )
    
    boundary_0 = rs.JoinCurves( [midCrv, crv_0, offs_0, crv_1] )[0]
    boundary_1 = rs.JoinCurves( [midCrv, crv_2, offs_1, crv_3] )[0]
    
    buildings_rechts = []
    buildings_links = []
    
    rs.Redraw()
    rs.UnselectAllObjects()
    rs.Command("SelBoundary SelectionMode=Crossing selID "+str(boundary_0)+" enter enter", 0)
    for obj in rs.SelectedObjects():
        if rs.ObjectType(obj) == 16 and rs.SurfaceVolume(obj)[0] > 6000:# "OSM::buildings::_bldg3D_srf":
            buildings_rechts.append( obj )
    
    rs.Redraw()
    rs.UnselectAllObjects()
    rs.Command("SelBoundary SelectionMode=Crossing selID "+str(boundary_1)+" enter enter", 0)
    for obj in rs.SelectedObjects():
        if rs.ObjectType(obj) == 16 and rs.SurfaceVolume(obj)[0] > 6000:# "OSM::buildings::_bldg3D_srf":
            buildings_links.append( obj )
    
    rs.Redraw()
    rs.UnselectAllObjects()
    #rs.SelectObjects( buildings_links )
    print "len(buildings_rechts)",len(buildings_rechts)
    print "len(buildings_links)",len(buildings_links)
    print "length(midCrv)", rs.CurveLength( midCrv )
    building_centers_and_IDs_rechts = []
    building_centers_and_IDs_links = []
    for objID in buildings_rechts:
        coords  = getSurfacePoints( objID )
        centerX = pntCentroid( coords )
        building_centers_and_IDs_rechts.append( [centerX, objID] )
    for objID in buildings_links:
        coords  = getSurfacePoints( objID )
        centerX = pntCentroid( coords )
        building_centers_and_IDs_links.append( [centerX, objID] )
    
    ### check auf nearest / geringster abstand
    nearest_centers_rechts =[]
    nearest_IDs_rechts = []
    nearest_centers_links =[]
    nearest_IDs_links = []
    
    for traeger_center in traeger_centers:
        testDist = 1000000 # 1mio meter, also sehr gross
        for item in building_centers_and_IDs_rechts:
            centerX = item[0]
            builingID = item[1]
            dist = rs.Distance(traeger_center, pnt2XY0(centerX))
            if dist < testDist:
                testDist = dist
                nearestCenter = centerX
                nearestID = builingID
                #print dist
        rs.SelectObject( nearestID )
        rs.AddLine( traeger_center, nearestCenter )
        nearest_centers_rechts.append( nearestCenter )
        nearest_IDs_rechts.append( nearestID )
    
    for traeger_center in traeger_centers:
        testDist = 1000000 # 1mio meter, also sehr gross
        for item in building_centers_and_IDs_links:
            centerX = item[0]
            builingID = item[1]
            dist = rs.Distance(traeger_center, pnt2XY0(centerX))
            if dist < testDist:
                testDist = dist
                nearestCenter = centerX
                nearestID = builingID
                #print dist
        rs.SelectObject( nearestID )
        rs.AddLine( traeger_center, nearestCenter )
        nearest_centers_links.append( nearestCenter )
        nearest_IDs_links.append( nearestID )
    if 0:
        nearest_centers_links = nearest_centers_links
        nearest_IDs_links = nearest_IDs_links
        nearest_centers_rechts = nearest_centers_rechts
        nearest_IDs_rechts = nearest_IDs_rechts
    return [nearest_centers_rechts, nearest_IDs_rechts, nearest_centers_links, nearest_IDs_links]

### DM2_w21 johanna theurl 
##########################

############################
### DM2_w21 demo UN_NYC_slab
unoX = [ 0.876922678230265,-0.480631476711064,0 ]
unoY = [ 0.480631476711061,0.876922678230267,0  ]
uno0 = [ 728.04, 532.76, 0.00 ]

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_w21 demo UN_NYC_slab
############################

################
######## basic library data and functions
################

######## 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(2021,12,24) - dayNumber2day
#print "btw : "+str(schlafTage)+" tage sinds noch bis weihnachten - und 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(0)
