@5uphi @7z99 @nicemike40 Ok so I finally got some time to work out a small demo script.
It’s a good exercise so you may want to try it yourself before looking at it.
I’ll put the full script below, just copy paste the whole thing into a script in workspace and press play.
The only important bit is the function calculateAngles(). It takes a random vector from your list of lookvectors, and then computes the signed angles of all vectors in the list relative to your randomly chosen one. You could just take the first one instead of a random one, they are randomly initiated in makeVectors() anyway, but just to prove it’s in no way due to order.
local angle = math.acos(myVector:Dot(randomVector))
local cross = myVector:Cross(randomVector)
if normal:Dot(cross) < 0 then
angle = -angle
end
I just implemented that calculation from the answer I linked earlier. Just like in your previous solution, there are other strategies possible, they may be faster, but it’s not very important I think. The important thing is this fixes the scaling, it’s now O(n).
In this example, we only have stuff in the horizontal plane, so I just manually define the normal vector as [0,1,0]. It’s not hard to compute the normal vector from 2 vectors, as long as they are not exactly aligned. The normal vector is the vector orthogonal to the plane, and since all the vectors are in the same plane, this should work. Just always re-use the same normal vector. (In the rare case that all vectors are aligned, any of them are the furthest apart).
Using the normal, we can compute the sign of the angle (the unsigned angle is computed as you did before). After that, we can just go through the list and find the lowest and highest angle. The maximum angle is 180 degrees, and so this should always work. It should also work for non-horizontal vectors, but you will have to compute the normal. I thought the example would be more clear this way.
Technically, since I take the unit vector of a random vector, it would crash if the random vector were [0,0,0] but it’s virtually impossible and just a demo script.
Anyway here is the full code… it’s very hastily written - please don’t judge X)
local myVectors = {}
local rng = Random.new()
local arrowLength = 10
local origin = Vector3.new(0,5,0)
local allArrows = Instance.new("Model")
local function makeVectors(amount)
for i=1, amount do
local x = rng:NextNumber()
local z = rng:NextNumber()*2-1
local myVector = Vector3.new(x, 0, z)
myVector = myVector.Unit
table.insert(myVectors, {Vector = myVector})
end
end
local function makeArrow()
local arrow = Instance.new("Model")
local mainPart = Instance.new("Part")
mainPart.Size = Vector3.new(0.05,0.05, arrowLength)
local leftHead = Instance.new("WedgePart")
leftHead.Size = Vector3.new(.05, .25, .5)
leftHead.CFrame = CFrame.new(-0.125,0, -arrowLength/2-.25) * CFrame.Angles(0, 0, math.pi/2)
local rightHead = Instance.new("WedgePart")
rightHead.Size = Vector3.new(.05, .25, .5)
rightHead.CFrame = CFrame.new(0.125,0, -arrowLength/2-.25) * CFrame.Angles(math.pi, math.pi, math.pi/2)
for i, myPart in {mainPart, leftHead, rightHead} do
myPart.Anchored = true
myPart.Color = Color3.new(0.737255, 0.219608, 0.152941)
myPart.Parent = arrow
end
arrow.PrimaryPart = mainPart
return arrow
end
local arrowTemplate = makeArrow()
--arrowTemplate.Parent = game.Workspace
local function displayVectors()
for i, vectorInfo in myVectors do
local myVector = vectorInfo["Vector"]
local arrow = arrowTemplate:Clone()
local myCF = CFrame.new(myVector*arrowLength/2 + origin, myVector*(arrowLength+1) + origin)
arrow:SetPrimaryPartCFrame(myCF)
arrow.Parent = allArrows
vectorInfo["Arrow"] = arrow
end
allArrows.Parent = game.Workspace
end
local function calculateAngles()
local randomIndex = rng:NextInteger(1, #myVectors)
local randomVectorInfo = myVectors[randomIndex]
local randomVector = randomVectorInfo["Vector"]
local normal = Vector3.new(0,1,0)
for i, vectorInfo in myVectors do
local myVector = vectorInfo["Vector"]
local angle = math.acos(myVector:Dot(randomVector))
local cross = myVector:Cross(randomVector)
if normal:Dot(cross) < 0 then
angle = -angle
end
vectorInfo["Angle"] = angle
end
end
local function findFurthest()
local lowest = nil
local lowestIndex = nil
local highest = nil
local highestIndex = nil
for i, vectorInfo in myVectors do
local myAngle = vectorInfo["Angle"]
if (not lowest) or (myAngle < lowest) then
lowest = myAngle
lowestIndex = i
end
if (not highest) or (myAngle > highest) then
highest = myAngle
highestIndex = i
end
end
return myVectors[lowestIndex], myVectors[highestIndex]
end
local function colorArrows(infoTable)
for i, info in infoTable do
local arrow = info["Arrow"]
for i, myPart in arrow:GetChildren() do
myPart.Color = Color3.new(0, 1, 0.498039)
end
end
end
makeVectors(29)
displayVectors()
calculateAngles()
local lowestInfo, highestInfo = findFurthest()
colorArrows({lowestInfo, highestInfo})