import rhinoscriptsyntax as rs
import random
rs.EnableRedraw(0)

delObjs = rs.AllObjects()
rs.DeleteObjects(delObjs)

# peak
layer_name = "GIS::DGM_5m"
objs = rs.ObjectsByLayer(layer_name)
rs.EnableObjectGrips(objs[0], 1)
coords = rs.ObjectGripLocations(objs[0])
rs.EnableObjectGrips(objs[0], 0)
peak = max(coords, key=lambda pt: pt[2])

rs.MoveObjects([o for o in rs.AllObjects()
                if o not in (rs.ObjectsByLayer("GIS::DGM_5m") or [])], (0,0,60))
## dm library einfuegen

# curvesurfaceintersection - verbindung zum dachstein anhand von stutzen

##### PROJEKT AMELIA EARHART
##### VERENA ULM GRUBEREINS

### ersten schritte fuer das projekt keine surfaces 
##### schritt eins wolkenbett
wolken_coords = []
#hilfe
# zufaelliger punkt auf einer kugeloberflaeche

def zufalls_kugelpunkt(zentrum, radius):
    v = rs.VectorCreate(
        [random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)],
        [0, 0, 0])
    if rs.VectorLength(v) == 0:v = [1,0,0]
    # normieren auf einheitsvektor befehl
    v = rs.VectorUnitize(v)
    # skalieren auf kugelradius
    v = rs.VectorScale(v, radius)
    # punkt = zentrum plus vektor
    return [zentrum[0] + v[0], zentrum[1] + v[1], zentrum[2] + v[2]]

# punktwolke auf einer kugel
def kugel_punktwolke(zentrum, radius=10, dichte=150):
    return [zufalls_kugelpunkt(zentrum, radius) for _ in range(dichte)]

# eine wolkeneinheit besteht aus mehreren ueberlagerte kugeln
def wolkeneinheit_erzeugen(zentrum, radius_basis, gruppenname):
    if not rs.IsGroup(gruppenname):
        rs.AddGroup(gruppenname)

    kugeln = random.randint(2, 5)
    vorheriges_zentrum = None

    for i in range(kugeln):
        radius = radius_basis * random.uniform(0.7, 1.25)

        if i == 0:
            c = zentrum [:]
        else:
            offset = [
            random.uniform(-radius * 0.55, radius * 0.55),
            random.uniform(-radius * 0.55, radius * 0.55),
            random.uniform(-radius * 0.07, radius * 0.07)
            ]
            c = [
            vorheriges_zentrum[0] + offset[0],
            vorheriges_zentrum[1] + offset[1],
            vorheriges_zentrum[2] + offset[2]
            ]
        pts = kugel_punktwolke(c, radius, dichte=punkte_pro_kugel)

        wolken_coords.extend(pts)
        pts_id = rs.AddPoints(pts)
        farbe = [random.randint(180, 200), random.randint(210, 230), random.randint(240, 255)]
        rs.ObjectColor(pts_id, farbe)
        rs.AddObjectsToGroup(pts_id, gruppenname)
        vorheriges_zentrum = c

### wichtige parameter
#hoehen_offset = 25               # wolkenhoehe
hoehe = random.uniform(40,50)
hoehe_var = 6 # kleine hoehenvariation
radius_basis_zentrum = 28  # grosse wolken
radius_basis = 20          # kleinere wolken
punkte_pro_kugel = 180     # punktdichte pro kugel

lenXmax = 250   # laenge
lenYmax = 220   # breite
lenZmax = 20   # hoehe
minFac = 0.8   # variation der volumina
anz_wolken_pro_cluster = 25  # wolkeneinheiten pro cluster

# cluster und zentren 
cluster_zentren = [
    [peak[0] - 60, peak[1] - 40, peak[2] + hoehe + random.uniform(-hoehe_var, hoehe_var)],
    [peak[0], peak[1], peak[2] + hoehe + random.uniform(-hoehe_var, hoehe_var)],
    [peak[0] + 60, peak[1] + 40, peak[2] + hoehe + random.uniform(-hoehe_var, hoehe_var)],
    [peak[0] + 180, peak[1] - 40, peak[2] + hoehe + random.uniform(-hoehe_var, hoehe_var)],
    [peak[0] + 320, peak[1] + 40, peak[2] + hoehe + random.uniform(-hoehe_var, hoehe_var)],
]

def cluster_erzeugen(zentrum):
    wolkeneinheit_erzeugen(zentrum, radius_basis_zentrum, "WolkenZentrum")
    # mehrere wolkeneinheiten in einem volumen um das zentrum
    for i in range(anz_wolken_pro_cluster):
        lenX = random.uniform(lenXmax * minFac, lenXmax)
        lenY = random.uniform(lenYmax * minFac, lenYmax)
        lenZ = random.uniform(lenZmax * minFac * 0.5, lenZmax * 0.8) # hoehenvariation minimal
        off = [
            random.uniform(-lenX * 0.5, lenX * 0.5),
            random.uniform(-lenY * 0.5, lenY * 0.5),
            random.uniform(-lenZ * 0.5, lenZ * 0.5)]
        c = [zentrum[0] + off[0],
             zentrum[1] + off[1],
             zentrum[2] + off[2]]
        wolkeneinheit_erzeugen(c, radius_basis, "Wolken")

# zentren erzeugen cluster
for cen in cluster_zentren:
    cluster_erzeugen(cen)
### fuellwolken sind zwischen den zentren fuer ein schoenes wolkenbett
min_x = min(c[0] for c in cluster_zentren) - 20
max_x = max(c[0] for c in cluster_zentren) + 20
min_y = min(c[1] for c in cluster_zentren) - 35
max_y = max(c[1] for c in cluster_zentren) + 35

rs.EnableRedraw(True)
rs.ZoomExtents()

## peak fuer plattform
wolken_pts = (rs.ObjectsByGroup("Wolken") or []) + (rs.ObjectsByGroup("WolkenZentrum") or [])
wolken_pts = [pid for pid in wolken_pts if rs.IsPoint(pid)]
if not wolken_pts:
    print("Keine wolkenpunkte")
else:
    wolken_peak=max([rs.PointCoordinates(pid) for pid in wolken_pts], key=lambda p: p[2])
    rs.AddPoint(wolken_peak)
    print("Wolken-Peak", wolken_peak)

### schritt zwei 
### die plattform 
# layer
rs.AddLayer("LandeplattformAussen", color=(80,80,80))
rs.CurrentLayer("LandeplattformAussen")

# parameter fuer die plattform
plattform_breite = 35 # breite in x richtung
plattform_tiefe = 30   # breite in yrichtung
linien_abstand = 0.3
# verlaengerung in xrichtung  nachtraegliche ausbesserung 
# plattform wird auf beiden seiten verlaengert fuer fluglandeplatz
verlaengerung_x = 25       
gesamt_breite_x = plattform_breite + 2 * verlaengerung_x

# definieren der rechteckpunkte zentriert
# vier eckpunkte
# rechteck ist 101meter in x und 30m in y
cx = peak[0]   
cy = peak[1]
plattform_hoehe = wolken_peak[2] +6
pA = [cx-gesamt_breite_x/2, cy-plattform_tiefe/2, plattform_hoehe]
pB = [cx+gesamt_breite_x/2, cy-plattform_tiefe/2, plattform_hoehe]
pC = [cx+gesamt_breite_x/2, cy+plattform_tiefe/2,  plattform_hoehe]
pD = [cx-gesamt_breite_x/2, cy+plattform_tiefe/2,  plattform_hoehe]

# zeichnen des rechtecks als polyline
rs.AddPolyline([pA, pB, pC, pD, pA])

# verdichtung eins
# raster 
#anzahl der linien horizontal in die yrichtung
anzahl = int(plattform_tiefe / linien_abstand) + 1

# farbverlauf 
# abhaengig vom abstand zur mitte wird die linie heller oder dunkler
for i in range(anzahl):
    # YPosition der Linie 
    y = pA[1] + i * linien_abstand
    line_id = rs.AddLine([pA[0], y, plattform_hoehe], [pB[0], y, plattform_hoehe])
    # abstand von der mitte in Yrichtung null mitte eins rand
    dist = min(1.0, abs(y-cy)/(plattform_tiefe/2.0))
    grau = int(80 + dist * (200 - 80))  # mitte 80 dunkel rand 200 hell
    rs.ObjectColor(line_id, (grau, grau, grau))

# verdichtung zwei 
# kreuzlinien in x richtung
anzahl2 = int(gesamt_breite_x / linien_abstand) + 1

# farbverlauf
for j in range(anzahl2):
    x = pA[0] + j * linien_abstand
    line_id = rs.AddLine([x, pA[1], plattform_hoehe], [x, pD[1], plattform_hoehe])
    # abstand von der mitte in x richtung null mitte eins rand
    dist = min(1.0, abs(x-cx)/(gesamt_breite_x/2.0))
    # grauverlauf innen dunkler aussen heller
    grau = int(80 + dist * (200 - 80))
    rs.ObjectColor(line_id, (grau, grau, grau))

### ende landeplattform

### schritt drei
# markierungsstriefen entlang der landebahn
# zwei randstreifen und ein mittelstreifen

# Layer
rs.AddLayer("Markierungsstreifen", color=(100,180,255))
rs.CurrentLayer("Markierungsstreifen")

# parameter
mark_offset = 2.0 # die distanz von plattformrand und randlinie da nach innen versetzt
mark_halfwidth = 0.35 # halbe streifenbreite 
mark_z = plattform_hoehe + 0.05 # liegt etwas ueber der plattform fuer sichtbarkeit

# plattformgrenzen schon festgelegt siehe schritt zwei
x_min = pA[0]
x_max = pB[0]
y_min = pA[1]
y_max = pD[1]

# drei linienpositionen in y links mitte rechts
y_left_mark = y_min + mark_offset
y_mid_mark = cy
y_right_mark = y_max - mark_offset

def add_strip(x0, x1, y_center, half_w, z):
    # streifen als duennes rechteck 
    # geschlossene polyline
    A = [x0, y_center - half_w, z]
    B = [x1, y_center - half_w, z]
    C = [x1, y_center + half_w, z]
    D = [x0, y_center + half_w, z]
    crv = rs.AddPolyline([A, B, C, D, A])
    return crv

# markierungsstreifen erzeugen
s1 = add_strip(x_min, x_max, y_left_mark, mark_halfwidth, mark_z) # linker Randstreifen
s2 = add_strip(x_min, x_max, y_mid_mark, mark_halfwidth, mark_z) # Mittellinie
s3 = add_strip(x_min, x_max, y_right_mark, mark_halfwidth, mark_z) # rechter Randstreifen

# farbe weiss 
for sid in [s1, s2, s3]:
    if sid: rs.ObjectColor(sid, (255,255,255))

## schritt vier
## flugzeughangar klein fuer amelia earhart
# hangar
rs.AddLayer("hangar", color=(150,150,150))
rs.CurrentLayer("hangar")

# Abmessungen
hangar_breite_y = plattform_tiefe * 1.2 # ueberlappt links und rechts leicht
hangar_tiefe_x = plattform_tiefe * 1.4 # tiefe 
hangar_hoehe = 10 # hoehe stuetze
dach_hoehe = hangar_hoehe * 0.4 # hoehe dach

# hangar befindet sich zur haelfte auf plattform 
# position 
x_front = cx - (gesamt_breite_x / 2.0) # befindet sich auf plattformkante links
# hangar geht von dort weg in x 
#rueckseite 
x_back = x_front - hangar_tiefe_x

# in y zentriert ueberlappt etwas
y_left = cy - hangar_breite_y / 2.0 #linke seite von hangar
y_right = cy + hangar_breite_y / 2.0 # rechte seite von hangar
y_mid = cy # mitte first

# hoehe fuer stuetzen und dach
z0 = plattform_hoehe # start der stuetzen liegt auf plattform
z_top = z0 + hangar_hoehe # oberkante traufe
z_ridge = z_top + dach_hoehe # firsthoehe

# rippen
anzahl_rippen = 26
abstand_x = hangar_tiefe_x / (anzahl_rippen - 1) # gleichmaessige verteilung der rippen

#fachwerk rippen erzeugen
for i in range(anzahl_rippen):
    # x position der rippe startet vorne und geht nach hinten
    x = x_front - i * abstand_x
    # stuetzen zwei senkrechte stuetzen pro rippe
    rs.AddLine([x, y_left, z0], [x, y_left, z_top])
    rs.AddLine([x, y_right, z0], [x, y_right, z_top])
    # dachtraeger zum first ein dreieck zwei schraege linien
    rs.AddLine([x, y_left, z_top], [x, y_mid, z_ridge])
    rs.AddLine([x, y_right, z_top], [x, y_mid, z_ridge])
    # aussteifung
    # obergurt oben und kurze diagonalen 
    rs.AddLine([x, y_left, z_top], [x, y_right, z_top])
    # aussteifung in der mitte
    rs.AddLine([x, y_mid, z_top], [x, y_mid, z_ridge])


### schritt fuenf
### hangar haut 
## flaeche aus polylines

rs.AddLayer("hangarhaut", color=(100,180,255))
rs.CurrentLayer("hangarhaut")

# gleiche farbe wie das kreuz und verlaufend
base_col = (100, 180, 255) # grundfarbe

# start schleife eine funktion def col verlauf die aus yposition eine farbe erzeugt fuer farbverlauf
def col_verlauf(y):
    # null am Rand und eins in der mitte
    t = 1.0 - abs(y - y_mid) / (hangar_breite_y/2.0)
    if t < 0: t = 0 # am rand dunkler
    if t > 1: t = 1 # in der mitte heller
    r = int(base_col[0] + t * (255 - base_col[0]))
    g = int(base_col[1] + t * (255 - base_col[1]))
    b = int(base_col[2] + t * (255 - base_col[2]))
    return (r, g, b) # je naeher zur mitte desto heller

# xpositionen aller rippen 
rib_x = [x_front - i * abstand_x for i in range(anzahl_rippen)] # erstellen einer list
# dachhaut linien laengs ueber die rippen
roof_div = 40 # linien zwischen traufe und first

# schleife fuer die linke dachflaeche 
for k in range(roof_div + 1):
    f = float(k) / float(roof_div)
    pts = []
    for x in rib_x:
        y = y_left + f * (y_mid - y_left)
        z = z_top + f * (z_ridge - z_top)
        pts.append([x, y, z])
    crv = rs.AddPolyline(pts)
    rs.ObjectColor(crv, col_verlauf(y)) # polyline wird gezeichnet und faerbt sie abhaengig von y 

# schleife fuer die rechte dachflaeche gleiches prinzip
for k in range(roof_div + 1):
    f = float(k) / float(roof_div)
    pts = []
    for x in rib_x:
        y = y_right + f * (y_mid - y_right)
        z = z_top + f * (z_ridge - z_top)
        pts.append([x, y, z])
    crv = rs.AddPolyline(pts)
    rs.ObjectColor(crv, col_verlauf(y))

wall_div = 40 # linien fuer die wand
# linke wand y
for k in range(wall_div + 1):
    f = float(k) / float(wall_div)
    z = z0 + f * (z_top - z0)
    pts = [[x, y_left, z] for x in rib_x]
    crv = rs.AddPolyline(pts)
    rs.ObjectColor(crv, col_verlauf(y_left))
# rechte wand y 
for k in range(wall_div + 1):
    f = float(k) / float(wall_div)
    z = z0 + f * (z_top - z0)
    pts = [[x, y_right, z] for x in rib_x]
    crv = rs.AddPolyline(pts)
    rs.ObjectColor(crv, col_verlauf(y_right))
    
back_div_y = 20 # rueckseite hinten 
back_div_z = 22

# horizontale linien hinten
for j in range(back_div_z + 1):
    fz = float(j) / float(back_div_z)
    z = z0 + fz * (z_top - z0)
    pts = []
    for i in range(back_div_y + 1):
        fy = float(i) / float(back_div_y)
        y = y_left + fy * (y_right - y_left)
        pts.append([x_back, y, z])
    crv = rs.AddPolyline(pts)
    rs.ObjectColor(crv, col_verlauf(y_mid))

# vertikale linien hinten mit verlauf heller in mitte
# jede linie wird je nach ylage gefaerbt
for i in range(back_div_y + 1):
    fy = float(i) / float(back_div_y)
    y = y_left + fy * (y_right - y_left)
    crv = rs.AddLine([x_back, y, z0], [x_back, y, z_top])
    rs.ObjectColor(crv, col_verlauf(y))

