######################################
###  DM2_w25 # AGruber@tugraz.at   ###
### hu_08 Mary Poppins Regenschirm ###
######################################

import rhinoscriptsyntax as rs
import random, math
import DM_lib as dm
import scriptcontext as sc

rs.UnitSystem(4)  # Meter
rs.ShowGrid(None, False)
rs.ShowGridAxes(None, False)
rs.ViewDisplayMode(rs.CurrentView(), "wireframe")
rs.Command("cplane w t enter", False)  # World Top

dm.PointRadius(displayModeX=0, rad=3, styl=3)
dm.PointRadius(displayModeX=1, rad=4, styl=1)
dm.PointRadius(displayModeX=2, rad=2, styl=0)
dm.printDisplay(state=False)

rs.EnableRedraw(False)

# -------------------------
# Funktion fuer Sockel-Panels
# -------------------------
def drawSockelPanel(panel, sockel=10):
    if len(panel) != 4:
        return
    p0, p1, p2, p3 = panel
    vX = rs.VectorSubtract(p1, p0)
    vY = rs.VectorSubtract(p3, p0)
    panel_center = rs.PointAdd(p0, rs.VectorScale(rs.VectorAdd(vX, vY), 0.5))
    # Sockel-Panels kein Kreis
    if p0[2] < 2 * sockel:
        num_lines = 50
        for i in range(num_lines):
            u, v = random.random(), random.random()
            edge_point = rs.PointAdd(p0, rs.VectorAdd(rs.VectorScale(vX, u), rs.VectorScale(vY, v)))
            line = rs.AddLine(edge_point, panel_center)
            if line:
                rs.ObjectColor(line, [50,50,50])
        return
    # sonst Kreismuster
    rel_pos = [(random.uniform(0.2,0.8), random.uniform(0.2,0.8)) for _ in range(3)]
    centers = [rs.PointAdd(p0, rs.VectorAdd(rs.VectorScale(vX,u), rs.VectorScale(vY,v))) for (u,v) in rel_pos]
    r = max(0.1 * rs.VectorLength(vX), 0.01)
    circle_data = []
    for c in centers:
        normal = rs.VectorUnitize(rs.VectorCrossProduct(vX,vY))
        plane = rs.PlaneFromNormal(c, normal)
        plane = rs.RotatePlane(plane, 90, normal)
        circle = rs.AddCircle(plane, r)
        if circle:
            rs.ObjectColor(circle, [200,100,150])
            circle_data.append((c, plane, circle))
    edges = [(p0,p1),(p1,p2),(p2,p3),(p3,p0)]
    edge_mids = [rs.PointAdd(a, rs.VectorScale(rs.VectorSubtract(b,a),0.5)) for (a,b) in edges]
    for center, plane, circle in circle_data:
        edge_point = rs.PointAdd(center, rs.VectorScale(plane.XAxis,r))
        for mid in edge_mids:
            line = rs.AddLine(edge_point, mid)
            if line:
                rs.ObjectColor(line, [150,150,150])
    for i in range(len(circle_data)):
        for j in range(i+1,len(circle_data)):
            ci, plane_i, _ = circle_data[i]
            cj, plane_j, _ = circle_data[j]
            line = rs.AddLine(rs.PointAdd(ci, rs.VectorScale(plane_i.XAxis,r)),
                              rs.PointAdd(cj, rs.VectorScale(plane_j.XAxis,r)))
            if line:
                rs.ObjectColor(line, [100,100,100])

# -------------------------
# Funktion Regenschirm
# -------------------------
def create_umbrella_with_panels(position=[0,0,0], radius=0.5, pole_height=20, num_ribs=8, circle_drop=0.8, rib_drop=0.1, pole_length=0.6):
    pos3d = rs.coerce3dpoint(position)
    pos3d = rs.PointAdd(pos3d,[0,0,-pole_height])
    top = rs.VectorAdd(pos3d,[0,0,pole_height])

    # Kreis der Strebenpunkte
    circle_z = pole_height*circle_drop
    streben_top = []
    streben = []
    for i in range(num_ribs):
        angle = 2*math.pi*i/num_ribs
        x = radius*math.cos(angle)
        y = radius*math.sin(angle)
        z = circle_z
        pt = rs.VectorAdd([x,y,z], pos3d)
        # Strebenpunkte aufteilen in drei Segmente
        mid1 = [(top[j]+pt[j]*2)/3 for j in range(3)]
        mid2 = [(top[j]+pt[j]*1)/3 for j in range(3)]
        streben.append([top, mid1, mid2, pt])
        streben_top.append(pt)

    # Streben zeichnen
    for s in streben:
        rs.AddCurve(s, degree=2)

    # Querlinien
    num_segments = 3
    for seg in range(1,num_segments):
        for i in range(len(streben)):
            next_i = (i+1)%len(streben)
            rs.AddLine(streben[i][seg], streben[next_i][seg])

    # Schirmkante
    streben_top.append(streben_top[0])
    rs.AddCurve(streben_top,1)

    # Panels zwischen Streben & Querlinien
    for seg in range(num_segments):
        for i in range(len(streben)):
            next_i = (i+1)%len(streben)
            if len(streben[i]) < 4 or len(streben[next_i]) < 4:
                continue
            panel = [streben[i][seg], streben[next_i][seg], streben[next_i][seg+1], streben[i][seg+1]]
            drawSockelPanel(panel)

    # Stab
    bottom = rs.PointAdd(top,[0,0,-pole_length])
    rs.AddLine(top,bottom)

    return bottom

# -------------------------
# Flugbahn
# -------------------------
flugbahn = rs.ObjectsByName("flugbahn")[0]
anzahl = 20
positions = rs.DivideCurve(flugbahn, anzahl, False)

dm.newEmptyLayer("PROJECT::umbrellas",[0,100,200])
rs.Redraw()

delta_circle_drop = dC = (0.9-0.1)/anzahl
delta_rib_drop = dR = (0.6-0.2)/anzahl
delta_radius = dRad = (20.0-0.1)/anzahl
pole_start = 0.8
pole_end = 20.0
delta_pole = (pole_end-pole_start)/anzahl

# Block auf Layer "Mary popins"
layer_name = "Mary popins"
objs_on_layer = rs.ObjectsByLayer(layer_name)
block_instance = None
for obj in objs_on_layer:
    if rs.IsBlockInstance(obj):
        block_instance = obj
        break
if not block_instance:
    raise Exception("Keine Blockinstanz auf Layer '{}' gefunden.".format(layer_name))

block_def_name = rs.BlockInstanceName(block_instance)
block_def_objs = rs.BlockObjects(block_def_name)
block_def_point = None
for obj in block_def_objs:
    if rs.IsPoint(obj):
        block_def_point = rs.PointCoordinates(obj)
        break
if not block_def_point:
    raise Exception("Kein Punkt innerhalb der Blockdefinition gefunden!")

xform = rs.BlockInstanceXform(block_instance)
block_point_inst = rs.PointTransform(block_def_point,xform)

stop_distance = 200
end_pos = positions[-1]

last_umbrella_objs = []
last_block = None
block_stop = False

# -------------------------
# Animation
# -------------------------
for i,pos in enumerate(positions):
    if sc.escape_test():
        print("Animation abgebrochen!")
        break

    if last_umbrella_objs:
        rs.DeleteObjects(last_umbrella_objs)
        last_umbrella_objs = []

    if last_block and not block_stop:
        rs.DeleteObject(last_block)
        last_block = None

    objs_before = set(rs.AllObjects())
    bottom = create_umbrella_with_panels(
        position=pos,
        radius=0.1+dRad*i,
        num_ribs=8+i,
        circle_drop=0.9-dC*i,
        rib_drop=0.2+dR*i,
        pole_length=pole_start+delta_pole*i
    )
    objs_after = set(rs.AllObjects())
    last_umbrella_objs = list(objs_after - objs_before)

    dist_to_end = rs.Distance(bottom,end_pos)
    if not block_stop:
        if dist_to_end > stop_distance:
            target_point_on_stab = bottom
            new_block = rs.CopyObject(block_instance)
            translation = rs.VectorCreate(target_point_on_stab, block_point_inst)
            rs.MoveObject(new_block, translation)
            last_block = new_block
        else:
            target_point_on_stab = bottom
            new_block = rs.CopyObject(block_instance)
            translation = rs.VectorCreate(target_point_on_stab, block_point_inst)
            rs.MoveObject(new_block, translation)
            last_block = new_block
            block_stop = True

    rs.Redraw()
    rs.Sleep(100)

rs.EnableRedraw(True)
dm.printDisplay(state=True,scale=1000)
dm.newEmptyLayer("Default")
print("done")
