-- Author:JnJ Modding The Collaboration
-- Name:SplineInObject
-- Description: places objects along a spline - Platziert Objekte entlang eines Splines
-- Icon:iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAA3NCSVQICAjb4U/gAAACi0lEQVQokV2RS2yMYRSG3+82t79undJSFKOE0oimTVMqIu4VhIVYEIlm2oWEsCKxkwhhJ5EQG1JNNURalx0b4taimYqoTlVMo1PSVi8z88/3f9+xGCXxbs7mefKenMOICAAAYz3BOYE11DfO5Plg6WVl5QRomZFSRqPRHMb+CWQF44b0rIL8kDfNtdTZ9ZxbcJ/UWkcikf8F7VkuOYdOuoMFrAgMyYFnmTFWsnI9ESmlchjHVLKetgCDJWmlEEKylpuX2PiwlPIvDUDmhgU4QAABDBYgz8q9h6Jsch4MrCAO9sdwichYa9wfmUnPUpbcQa/fZijpfSnZv6+sfu+knXBJ01SkAtI8s/H85rG8kUCVYsKXnjGsRqcZmXS2l3sDDs+O+Hzz/xZIDeLA29hLlPkRchfGl3wP/kw7fY4bYAsTG140kGvgy60May33kBWk5hYW+xX3DzmJK9/SchwCro+q45Eax+35/BHEARBRR2eHDGo/qVRiJCEKZd6MoC6l0PBME3JLvi095lTWNZYy4X7sfmdUqK29vTvWLaE8QjC43Ddd543eSxeXFGcbhkebTCMd3FK5mjPDyLx+dP9R7EuuhLO09WxqtleckqmidfPFAhHePyc8VNTzJBHgv143t5Jrd26qkgx7dtUJWNn75kiktunsonN1Kwp7k/Gag4ezXCnL1p6uYEdrvsbjFUwJlgWZhw/ayBpeaFon+q5/ev+piE1UL45Y+ALEBUxPrJfIOXDmlOT61fsPBpyYEMrPg+TYxIWrNy7qIFmrGQcYwOWdu81PY1+JBHH/7add4MKCVVRWyZ/KC+v+2jXhy7cf79i2e9XUh3bt3Hri+Mmgkjy84NqtloDIXRa/AfS3RCEVjlBtAAAAAElFTkSuQmCCAAAAAACoDUtoAA0AgPiUn4f+fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-- Hide: no
-- Version 1.3 (2017-01-12)

-- Parameter section 
local objectDistance = 5                  -- distance between objects -- Abstand der Objekte --
local noObjectAtStart = true                -- no object at start of spline -- kein Objekt am Anfang --
local stayUpright = false                   -- objects stay upright -- Objekte stehen immer gerade (y nach oben) --
local useDistanceTable = false              -- use distanceTable instead of objectDistance - be careful -- verwende Tabelle mit Abstaenden anstatt objectDistance - sei vorsichtig! --
local distanceTable = {6,1.50,0.2}          -- distanceTable, has to be set for each object, too many entrys don't mind -- Tabelle mit den Abstaenden fuer jedes Objekt, zu viele Eintraege schaden nicht , zu wenig fuehren zu Fehlern
local randomOrder = true                   -- order of objects is choosen by random -- Die Reihenfolge der Objekte ist zufallsgesteuert
local randomObjectDistance = false          -- ranomizes distance between objects -- Abstaende zwischen den Objekten werden zufallsgesteuert
local randomObjectDistancePercentage = 50  -- percentage of objectDistance -- Variation des Abstands in Prozent der objectDistance --
local randomPlacement = false              -- places the object away from the spline by random choosen displacement -- Zufallsgesteuerte Position abseits des Splines --
local randomPlacementDistance = 5       -- dislocation distance from spline by randomPlacement -- Abstand zum Spline (x-z-Ebene) bei zufaelliger Platzierung
local randomYrotation = false              -- random rotation of y-Axis -- zufaellige Rotation um y-Achse
local internalXrotation = 0                 -- internal rotation of x-Axis in degrees -- interne Rotation um x-Achse in Grad
local internalYrotation = 0                -- internal rotation of y-Axis in degrees -- interne Rotation um y-Achse in Grad
local internalZrotation = 0                 -- internal rotation of z-Axis in degrees -- interne Rotation um z-Achse in Grad
local randomScale = false                   -- random scale of objects -- zufaellige Skalierung der Objekte --
local randomScaleLowLimX = 0.9              -- lower limit of scaleX -- untere Grenze der x-Skalierung --
local randomScaleHighLimX = 1.1             -- upper limit of scaleX -- obere Grenze der x-Skalierung --
local randomScaleLowLimY = 0.5              -- lower limit of scaleY -- untere Grenze der y-Skalierung --
local randomScaleHighLimY = 1.1               -- upper limit of scaleY -- obere Grenze der y-Skalierung --
local randomScaleLowLimZ = 0.8                -- lower limit of scaleZ -- untere Grenze der z-Skalierung --
local randomScaleHighLimZ = 1               -- upper limit of scaleZ -- obere Grenze der z-Skalierung --
local alignWithTerrain = true               -- aligns objects with terrain -- Setzt die Objekte auf's Terrain --
local notAtTerrainZero = false               -- if alignWithTerrain place no objects at height zero, which will be the result for objects outside the map -- wenn alignWithTerrain gewaehlt wurde, werden keine Objekte auf Hoehe Null gesetzt (das ist der Fall, wenn die Objekte ausserhalb der Map sind) --

-- don't forget to save modifications -- nach Aenderungen Script speichern -- 
-- not all settings can be combined   -- nicht alle Einstellungen sind miteinander kompatibel --
-- it can be useful to save the parameter section in an editor -- es kann nuetzlich sein, die Parameter-Sektion mit einem Editor zu speichern --

-- ChangeLog
  -- Version 1.3
    -- internal rotations: Possibilty to rotate placed object to aline objects which are not oriented in z direction -- Interne Rotationen, die das Objekt um seine eigenen Achsen dreht (fuer Objekte, die nicht in z-Richtung schauen)    
  -- Version 1.2
    -- Check if placed objects have heigt zero, then they are probably outside map -- Test, ob Objekte Hoehe Null haben, dann sind sie wahrscheinlich ausserhalb der map  -- 
    -- random scale of objects within LowLim..HighLim -- zufaellige Skalierung der Objekte innerhalb der Grenzen LowLim..HighLim --
  -- Version 1.1
    -- random distance - Ready in Version 1.1
    -- random placement radius - Ready in Version 1.1
    -- align to terrain - Ready in Version 1.1
    -- random rotation - Ready in Version 1.1
 
-- To Do
  -- solve problems with rotated parent TGs, is not as trivial as thought - Berechnung der Rueckrotation, wenn die Weltrotation von SplinePlacement nicht Null ist - ist wegen der nicht vertauschbaren Transformation nicht trivial -
 
-- Code section -- DO NOT MODIFY CODE BELOW ---- 

if objectDistance <= 0 then
    print("ERROR: Please use positive value for 'objectDistance' !")
    return
end

local node = getSelection(0)
if node == 0 or getName(node) ~= "splinePlacement" then
    print("ERROR: Please select 'splinePlacement' transform group!")
    return
end
local mySpline = getChild(node,"spline")
if mySpline == 0 then
    print("ERROR: 'spline' not present!")
    return
end
local objectsToPlace = getChild(node,"objectsToPlace")
if objectsToPlace == 0 then
    print("ERROR: 'objectsToPlace' not present!")
    return
end
local numObjectsToPlace = getNumOfChildren(objectsToPlace)
if numObjectsToPlace <= 0 then
    print("ERROR: 'objectsToPlace' has no objects!")
    return
end
local placedObjects = getChild(node,"placedObjects")
if placedObjects == 0 then
    print("ERROR: 'objectsToPlace' not present!")
    return
end
local splineLength = getSplineLength(mySpline)
if splineLength <= 0 then
    print("ERROR: 'spline' has zero length or is not a spline!")
    return
end
local myParent = getParent(node)
if myParent == 0 then
    print("ERROR: 'splinePlacement' has no parent (why ever?)!")
    return
end

-- wird fuer alignWithTerrain gebraucht
local root = getRootNode();
local map = getChildAt(root, 0);
local terrain = getChild(map, "terrain");


local xParent, yParent, zParent = getWorldTranslation(myParent)
local RxParent, RyParent, RzParent = getWorldRotation(myParent)
print(string.format("  Info: Parent Position %f, %f, %f -- Rotation %f, %f, %f ",xParent, yParent, zParent, RxParent, RyParent, RzParent))
-- To Do: fix rotations of parent groups - Rueckrotation der erzeugenden Gruppen
setRotation(placedObjects,RxParent, -RyParent, RzParent) -- loest Problem mit y-Drehungen, Drehungen um x und z sind nach wie vor schwierig
-- if true then return end

local iObject = 0   -- Nummer des Objekts in objectsToPlace
if randomOrder then
	iObject = math.floor(math.random()*numObjectsToPlace)
    -- print (iObject)
end

local splinePos = 0 -- Position entlang dem spline [0..1]
if noObjectAtStart then
    if randomObjectDistance then
        splinePos = objectDistance*(1+(math.random()-0.4)*randomObjectDistancePercentage/100)/splineLength -- mehr in Vorwaertsrichtung
    else 
        splinePos = objectDistance/splineLength -- splinePos um 1 Einheit erhoehen
    end
    if useDistanceTable then
        if randomObjectDistance then
            splinePos = distanceTable[iObject]*(1+(math.random()-0.4)*randomObjectDistancePercentage/100)/splineLength
        else 
            splinePos = distanceTable[iObject]/splineLength -- splinePos um 1 Einheit erhoehen
        end
    end -- if useDistanceTable
end

if splinePos < 0 then 
	splinePos = 0 
end
if splinePos > 1 then 
	print("ERROR: splinePos > 1 at beginning! Check parameters")
	return 
end

local xlast, ylast, zlast = getSplinePosition(mySpline, 0);    -- Position des letzten objekts
while splinePos <= 1 do
    local placeId = true
	local x, y, z = getSplinePosition(mySpline, splinePos);
    if randomPlacement then --- x und z variieren
        x = x + (math.random()-0.5)*randomPlacementDistance*2
        z = z + (math.random()-0.5)*randomPlacementDistance*2
    end
    if alignWithTerrain then
        y = getTerrainHeightAtWorldPos(terrain, x, y, z);
        if y == 0 and notAtTerrainZero then -- remove object, probably outside the map - Objekt loeschen, ist vermutlich ausserhalb der Map -
            placeId = false
        end;
        -- print(y)
    end 

	local rx, ry, rz = getSplineOrientation(mySpline, splinePos, 0, -1, 0);  
    -- print(string.format("  Info: Position %f, %f, %f -- Rotation %f, %f, %f",x, y, z, rx, ry, rz))

    if randomOrder then
        iObject = math.floor(math.random()*numObjectsToPlace)
        -- print (iObject)
        -- return
    else
        iObject = iObject + 1 -- naechstes Objekt nehmen
        if iObject >= numObjectsToPlace then 
            iObject = 0
        end
    end -- if randomOrder

    if placeId then -- place object 
        local myId = clone(getChildAt(objectsToPlace,iObject), false, true)
        link(placedObjects,myId)
        setTranslation(myId, x-xParent, y-yParent, z-zParent);
        if randomYrotation then
            if stayUpright then
                rx = 0
                rz = 0
            end
            ry = math.random()*math.pi*2
            setRotation(myId, rx, ry, rz);
        else
            local yyy = y-ylast
            if stayUpright then
                yyy = 0
            end
            setDirection(myId,x-xlast,yyy,z-zlast,0,1,0)
        end 
        rotateAboutLocalAxis(myId,math.pi*internalZrotation/180,0,0,1)  -- erst z
        rotateAboutLocalAxis(myId,math.pi*internalYrotation/180,0,1,0)  -- dann y
        rotateAboutLocalAxis(myId,math.pi*internalXrotation/180,1,0,0)  -- zuletzt x
        if randomScale then -- set random scale, use rx,ry,rz 
            local spanne = randomScaleHighLimX - randomScaleLowLimX
            rx = math.random()*spanne+randomScaleLowLimX
            spanne = randomScaleHighLimY - randomScaleLowLimY
            ry = math.random()*spanne+randomScaleLowLimY
            spanne = randomScaleHighLimZ - randomScaleLowLimZ
            rz = math.random()*spanne+randomScaleLowLimZ
            setScale(myId,rx,ry,rz)
        end
    end -- if placeId

    xlast = x -- update last position
    ylast = y
    zlast = z

    yyy = objectDistance/splineLength
    -- print(yyy)
    if useDistanceTable then
        yyy = distanceTable[iObject+1]/splineLength -- splinePos um 1 Einheit erhoehen
    end -- if useDistanceTable
    if randomObjectDistance then
        yyy = yyy*(1+(math.random()-0.4)*randomObjectDistancePercentage/100)
    end
    -- print(yyy)
    splinePos = splinePos + yyy -- splinePos um 1 Einheit erhoehen
    if splinePos < 0 then -- kann durch randomObjectDistance negativ werden
        splinePos = 0 
    end

end -- while
-- setRotation(mySpline,0,0,0)


print("So weit so gut")