###################################
### DM2_w25 # AGruber@tugraz.at ###
### hu_07 UN_headquaters  NYC   ### CRVs 4 ROLLER.COASTER | SETUP
###################################


##############################
import rhinoscriptsyntax as rs
import random, time, sys   ###
sys.path.append("P:/")     ###
sys.path.append("P:/WWW/dx6419/dm2/") ### *your* path to DM_lib.py                                            
import DM_lib as dm        ### 
############################## dm.reload_lib(dm)

rs.UnitSystem(4)                                            # meters = 4, cm = 3 etc
rs.ShowGrid(None, False)                                    # grid > False = off
rs.ShowGridAxes(None, False)                                 # y/y/z axen display > False/True = off/on
rs.ViewDisplayMode(rs.CurrentView(), "wireframe")
rs.Command("cplane w t enter", False)				        # cPlane World Top
dm.PointRadius(displayModeX=0, rad=3, styl=3)               # 0 => wireframe | info: (.., verbose=1)
dm.PointRadius(displayModeX=1, rad=4, styl=1)               # 1 => shaded    | info: (.., verbose=1)
dm.PointRadius(displayModeX=2, rad=2, styl=0)               # 2 => rendered  | info: (.., verbose=1)
dm.printDisplay(state=False)                                # nomen est omen
rs.EnableRedraw(False)



###_________________________________________#
### basic settings for grid to fit UN_slab  #
###                                         # ! no need 2 change !
floors = H  = dm.H = 40                     # default=40 / incl roof
slabs  = L  = dm.L = 11                     # default=11
depth  = D  = dm.D =  4                     # default= 4 / division in building_depth
floorHeight = fH = dm.fH = 4.0              # default= 4.0 / 4.0*(H-1) = 156 meters
                                            #
#############                               # get gridCoords L*D*H = 1760
UnoGridCoords = dm.getUnoGridCoords()       # get from DM_lib as dm
#################___________________________#


#######################
### my little helper: ###############
### gC (get_Coord) im UN_grid !     #
def gC(L,D,H): return dm.gC(L,D,H)  #
#####################################

##################
depthVec  = dVec = rs.VectorUnitize(rs.VectorSubtract( gC(0, 0, 0), gC(0, 2, 0) ))
lengthVec = lVec = rs.VectorUnitize(rs.VectorSubtract( gC(0, 0, 0), gC(1, 0, 0) ))
##################



dm.newEmptyLayer("UNO::setUp", [120,120,140])
if 1: ### SETUP >> dont' exec @ homework !
    if 0: rs.AddPoints(UnoGridCoords)       ### just 4 reference
    if 1:
        rs.AddLine( gC(0, 0, 0), gC(10, 0, 0) )
        rs.AddLine( gC(0, 0, 0), gC(0,  3, 0) )
        rs.AddLine( gC(0, 0, 0), gC(0, 0, 39) )
        rs.ObjectColor(rs.AllObjects()[2], [0, 222, 0] ), rs.CurveArrows(rs.AllObjects()[0], 2)
        rs.ObjectColor(rs.AllObjects()[1], [222, 0, 0] ), rs.CurveArrows(rs.AllObjects()[1], 2)
        rs.ObjectColor(rs.AllObjects()[0], [0, 0, 222] ), rs.CurveArrows(rs.AllObjects()[2], 2)
        rs.ObjectPrintWidth( rs.AllObjects()[0:3], 0.5 )
        
        p0 = gC(  0, 0, 0)
        p1 = gC( 10, 0, 0)
        p2 = gC( 10, 3, 0)
        p3 = gC(  0, 3, 0)
        unoBaseCoords = [p0, p1, p2, p3, p0]
        unoBaseCrv = rs.AddCurve( unoBaseCoords, 1)
        unoTopCoords = [dm.gC(  0, 0, 39),
                        dm.gC( 10, 0, 39),
                        dm.gC( 10, 3, 39),
                        dm.gC(  0, 3, 39),
                        dm.gC(  0, 0, 39),
                       ]
        unoTopCrv = rs.AddCurve( unoTopCoords, 1)
        rs.ZoomExtents()
    if 0: ### long version
        coords = []
        for y in range(11):
            for z in range(0, 40, 2):
                for x in range(0, 4, 3):
                    coords.append( dm.gC(y,x,z) )
        rs.AddPoints( coords )
        dm.SetObjDisplayModeAllViewports(rs.AddPoints( coords ), displaymodeX=0, verbose=1)
    
    if 1: ### smart version / comprehension
        pnts = rs.AddPoints([ dm.gC(y,x,z) for y in range(11) for z in range(0, 40, 2) for x in range(0, 4, 3) ])
        dm.SetObjDisplayModeAllViewports(pnts, displaymodeX=2, verbose=0) ### 2 == rendered


    
    
    #### CAMERA
    #### set / get camera
    if 0:
        print ("ViewNames :"),
        print (rs.ViewNames())
        
        cam = gC(5, -20, 19.5)
        tar = gC(5, 0, 19.5)
        rs.AddLine( cam, tar )
        
        #dm.setCameraTarget( cam, tar, 500.0, 0, 0, 0)
        rs.ViewProjection(view='Perspective', mode=1)   ### 1 = parallel, 2 = perspective, 3 = two point perspective
        dm.getCameraTarget(view=rs.CurrentView(), verbose=1)
        #dm.setCameraTarget( [334.42526127, 493.18649387, 171.88081703], [756.84885751, 592.83573956, 82.0], lens=50.0, rota=0, upVec=[0,0,1] ) # ... danke, andi !





#######################################
################ HERE YOU GO AS YOU GO:
dm.newEmptyLayer("myPROJ", [111,111,111])

import rhinoscriptsyntax as rs
import math
import random


def myNewPanel(panel, sockel=10, num_circles=1, lines_per_circle=40, num_rects=5):
    def valid_point(pt):
        return pt is not None and all(c is not None and abs(c) < 1e6 for c in pt)

    def safe_add_line(start, end, color=None):
        if not valid_point(start) or not valid_point(end):
            return None
        try:
            line = rs.AddLine(start, end)
            if line and color:
                rs.ObjectColor(line, color)
            return line
        except:
            return None

    def rotate_point_z(pt, center, angle_deg):
        angle_rad = math.radians(-angle_deg)
        x = center[0] + math.cos(angle_rad)*(pt[0]-center[0]) - math.sin(angle_rad)*(pt[1]-center[1])
        y = center[1] + math.sin(angle_rad)*(pt[0]-center[0]) + math.cos(angle_rad)*(pt[1]-center[1])
        return [x,y,pt[2]]

    if not panel or len(panel)<4: return []

    p0,p1,p2,p3 = panel
    panel_center = rs.PointAdd(p0, rs.VectorScale(rs.VectorAdd(rs.VectorSubtract(p1,p0), rs.VectorSubtract(p3,p0)), 0.5))
    origin = p0
    created_objs = []

    lay_name = "panel_lines"
    if not rs.IsLayer(lay_name): rs.AddLayer(lay_name, [180,120,200])
    group_name = "panel_{}_{}_{}_{}".format(int(p0[0]*100), int(p0[1]*100), int(p0[2]*100), random.randint(0,9999))
    gid = rs.AddGroup(group_name)

    # Sockel-Panels
    if p0[2] < 2*sockel:
        for i in range(num_rects):
            factor = 1 - i*0.15
            angle = i*10
            pts = []
            for pt in [p0,p1,p2,p3]:
                rotated_pt = rotate_point_z(pt, panel_center, angle)
                new_z = panel_center[2] + (pt[2]-panel_center[2])*factor
                pts.append([rotated_pt[0],rotated_pt[1],new_z])
            for j in range(4):
                line = safe_add_line(pts[j], pts[(j+1)%4], color=[50,50,50])
                if line: created_objs.append(line)
        return created_objs

    # Kreise auf Panels
    vX = rs.VectorSubtract(p1,p0)
    vY = rs.VectorSubtract(p3,p0)
    uVec = rs.VectorUnitize(vX)
    vVec = rs.VectorUnitize(vY)

    for _ in range(num_circles):
        center_u = random.uniform(0.3,0.7)*rs.VectorLength(vX)
        center_v = random.uniform(0.3,0.7)*rs.VectorLength(vY)
        center = rs.PointAdd(origin, rs.VectorAdd(rs.VectorScale(uVec, center_u), rs.VectorScale(vVec, center_v)))
        radius = min(rs.VectorLength(vX), rs.VectorLength(vY))*0.4*random.uniform(0.7,1.0)
        for i in range(-lines_per_circle//2, lines_per_circle//2+1):
            x_offset = i*radius/(lines_per_circle/2)
            y_limit = math.sqrt(max(0,radius**2 - x_offset**2))
            top = rs.PointAdd(center, rs.VectorAdd(rs.VectorScale(uVec,x_offset), rs.VectorScale(vVec,y_limit)))
            bottom = rs.PointAdd(center, rs.VectorAdd(rs.VectorScale(uVec,x_offset), rs.VectorScale(vVec,-y_limit)))
            line = safe_add_line(top,bottom,color=[random.randint(100,200),random.randint(50,120),random.randint(150,250)])
            if line: created_objs.append(line)

    # Panel outline
    for j in range(4):
        line = safe_add_line([p0,p1,p2,p3][j], [p0,p1,p2,p3][(j+1)%4], color=[200,200,200])
        if line: created_objs.append(line)

    if created_objs:
        try:
            rs.AddObjectsToGroup(created_objs, gid)
            for o in created_objs: rs.ObjectLayer(o, lay_name)
        except:
            rs.ObjectColor(created_objs, [180,120,200])
    return created_objs

def spiral_curve(points, radius, number, circDivision, colorRGB, base_crv):
    crv = rs.AddCurve(points, 3)
    dom = rs.CurveDomain(crv)[1]
    anzahl = 50
    deltaAng = 360.0 / anzahl
    deltaParam = dom / float(number)
    allCoordLists = []

    for i in range(number + 1):
        param = deltaParam * i
        cor = rs.EvaluateCurve(crv, param)
        hVec = dm.vecCurvePerpXY(crv, param)
        hVec = rs.VectorScale(hVec, radius)
        tanX = rs.CurveTangent(crv, param)
        coords = []
        for a in range(anzahl):
            hVecX = rs.VectorRotate(hVec, -deltaAng*a, tanX)
            hPntX = rs.VectorAdd(cor, hVecX)
            coords.append(hPntX)
        allCoordLists.append(coords)

        cut_index = int(circDivision * 1.3)
        if cut_index < len(coords):
            segment = rs.AddCurve(coords[cut_index:], 1)
            rs.ObjectPrintWidth(segment, 1.3)
            rs.ObjectColor(segment, [80,80,80])
            mid = rs.CurveMidPoint(segment)
            if base_crv:
                tP = rs.CurveClosestPoint(base_crv, mid)
                bottom = rs.EvaluateCurve(base_crv, tP)
                strut = rs.AddLine(mid, bottom)
                rs.ObjectColor(strut, [200,200,200])
                rs.ObjectPrintWidth(strut, 0.5)

    for i in range(int(anzahl/2), anzahl):
        coords = [cList[i] for cList in allCoordLists]
        shell = rs.AddInterpCurve(coords, 3)
        rs.ObjectColor(shell, colorRGB)

    return crv

allPanels = []
for y in range(L-1):
    for z in range(0, H-1):
        total_layers = H - 1           
        middle_1 = total_layers // 2 - 1
        middle_2 = total_layers // 2
        if z in [middle_1, middle_2]:
            continue

        for x in range(D-1):
            p0 = gC(y, x, z)
            p1 = gC(y+1, x, z)
            p2 = gC(y+1, x, z+1)
            p3 = gC(y, x, z+1)
            allPanels.append([p0, p1, p2, p3])

# Draw panels
for i,panel in enumerate(allPanels):
    dm.esc()
    myNewPanel(panel,sockel=10)
    if i and i%10==0: rs.Redraw()

# Alle Kurven
curve_data = [
    {"name": "blueCurve", "layerColor": [0,120,255], "points": [
        gC(2,0,2), gC(8,-9,6), gC(12,5,12), gC(8,10,17),
        gC(-2,3,19), gC(-10,4,23), gC(-4,-10,27), gC(6,-14,30),
        gC(12,-8,34), gC(4,0,38)
    ], "radius": 4.0, "number": 34, "circDivision": 20},

    {"name": "violetCurve", "layerColor": [160,60,255], "points": [
        gC(2,0,2), gC(8,-4,6), gC(14,3,10), gC(5,10,14),
        gC(0,22,18), gC(-8,10,22), gC(-1,-2,26), gC(-8,-10,30),
        gC(8,-16,32), gC(0,0,38)
    ], "radius": 4.0, "number": 36, "circDivision": 18},

    {"name": "cyanCurve", "layerColor": [50,180,200], "points": [
        gC(2,0,0), gC(6,-3,6), gC(12,-5,12), gC(8,0,18),
        gC(-2,4,22), gC(-6,10,26), gC(0,12,32), gC(5,5,36),
        gC(2,0,38)
    ], "radius": 4.0, "number": 36, "circDivision": 18},
]

# Kurven erzeugen
for data in curve_data:
    dm.newEmptyLayer(data["name"], data["layerColor"])
    spiral_curve(
        points=data["points"],
        radius=data["radius"],
        number=data["number"],
        circDivision=data["circDivision"],
        colorRGB=data["layerColor"],
        base_crv=unoBaseCrv
    )

rs.EnableRedraw(True)
dm.printDisplay(state=True, scale=1000)
dm.zA(0.9)
dm.newEmptyLayer("Default")

tst = 1
if tst:
    dm.newEmptyLayer("UNO::setUp", [120,120,240])
    if 0: 
        rs.ObjectColor(rs.AddCurve( [dm.getUnoCoord(0,0,0), dm.getUnoCoord(0,0,39), dm.getUnoCoord(10,0,39), dm.getUnoCoord(10,0,0), dm.getUnoCoord(10,3,0), dm.getUnoCoord(0,3,0), dm.getUnoCoord(0,3,39), dm.getUnoCoord(10,3,39)], 1), [100,0,200])
        rs.ObjectPrintWidth( rs.AllObjects()[0], 1.0 )
        rs.ObjectColor(rs.AddLine( dm.getUnoCoord(0, 0, 0), dm.getUnoCoord(0, 3, 0) ), [222, 0, 0] )
        rs.CurveArrows(rs.AllObjects()[0], 2)
        rs.ObjectColor(rs.AddLine( dm.getUnoCoord(0, 0, 0), dm.getUnoCoord(2, 0, 0) ), [0, 222, 0] )
        rs.CurveArrows(rs.AllObjects()[0], 2)
        rs.ObjectColor(rs.AddLine( dm.getUnoCoord(0, 0, 0), dm.getUnoCoord(0, 0, 7) ), [0, 0, 222] )
        rs.CurveArrows(rs.AllObjects()[0], 2)
        rs.ZoomExtents()

    if 0:
        dm.newEmptyLayer("UNO::big", [100,110,200])
        for coords in BigPanelCoords[0]+BigPanelCoords[1]+BigPanelCoords[2]+BigPanelCoords[3]: rs.AddCurve( coords, 1 )
    if 0:
        dm.UN_slab(showAll=1)

    if 1:
        dm.newEmptyLayer("UNO::tst", [100,110,200])
        for i,panel in enumerate(allPanels):          ### +backPanels+upSidePanels
            dm.esc()
            if 1 or panel[0][2] >= 0:
                pass
                myNewPanel( panel, sockel=10 )
                if i and i%10==0:
                    rs.Redraw()




########## EOS / EndOfScript
rs.EnableRedraw(True)     ### 4_the_MACs
dm.eDup()                 ### delete duplicate objects 
dm.printDisplay(state=True, scale=1000)
dm.zA( 0.9 )
dm.newEmptyLayer("Default")

