local getmetatable = getmetatable local setmetatable = setmetatable local math_floor = math.floor local math_ceil = math.ceil local math_sqrt = math.sqrt local math_sin = math.sin local math_cos = math.cos local math_acos = math.acos local math_abs = math.abs local string_format=string.format local error=error local print=print local table_insert=table.insert local table_sort=table.sort local V2PoolCapacity = 512 local V2Pool = LuaExt.CreateTable(V2PoolCapacity, 0) local V2InPoolCount = 0 local V2InPoolMT = { __index = function(t, k) error('index on fixedpoint pool '..k) end, __newindex = function(t, k, v) error('newindex on fixedpoint pool '..k) end, } local FP = { V2 = {}, Math = {} } local RawValueScaler = 1000 local PI = 3.141 local Degree180 = 180 local Rad2Deg = Degree180 / PI local Deg2Rad = PI / Degree180 local V1MT = {} V1MT.__index = V1MT V1MT.__newindex = function(t, k, v) error('newindex on fixedpoint V1 '..k) end local function tonumberV1(self) return self[1] / RawValueScaler end local function trimV1(raw) raw = math_floor(raw + 0.5) return setmetatable({raw}, V1MT) end local function rawV1(raw) return setmetatable({raw}, V1MT) end local function newV1(num) local raw = math_floor(num * RawValueScaler + 0.5) return setmetatable({raw}, V1MT) end local function cloneV1(self) return setmetatable({self[1]}, V1MT) end function V1MT.__add(lhs, rhs) local xIsFP = getmetatable(lhs) == V1MT local yIsFP = getmetatable(rhs) == V1MT if xIsFP and yIsFP then return rawV1(lhs[1] + rhs[1]) elseif xIsFP then return trimV1(lhs[1] + rhs * RawValueScaler) elseif yIsFP then return trimV1(lhs * RawValueScaler + rhs[1]) else error('fixedpoint __add arguments type error') end end function V1MT.__sub(lhs, rhs) local xIsFP = getmetatable(lhs) == V1MT local yIsFP = getmetatable(rhs) == V1MT if xIsFP and yIsFP then return rawV1(lhs[1] - rhs[1]) elseif xIsFP then return trimV1(lhs[1] - rhs * RawValueScaler) elseif yIsFP then return trimV1(lhs * RawValueScaler - rhs[1]) else error('fixedpoint __sub arguments type error') end end function V1MT.__mul(lhs, rhs) local xIsFP = getmetatable(lhs) == V1MT local yIsFP = getmetatable(rhs) == V1MT if xIsFP and yIsFP then return trimV1(lhs[1] / RawValueScaler * rhs[1]) elseif xIsFP then return trimV1(lhs[1] * rhs) elseif yIsFP then return trimV1(lhs * rhs[1]) else error('fixedpoint __mul arguments type error') end end function V1MT.__div(lhs, rhs) local xIsFP = getmetatable(lhs) == V1MT local yIsFP = getmetatable(rhs) == V1MT if xIsFP and yIsFP then return trimV1(lhs[1] / rhs[1] * RawValueScaler) elseif xIsFP then return trimV1(lhs[1] / rhs) elseif yIsFP then return trimV1((lhs * RawValueScaler) / rhs[1] * RawValueScaler) else error('fixedpoint __div arguments type error') end end function V1MT.__mod(lhs, rhs) return rawV1(lhs[1] % rhs[1]) end function V1MT.__unm(v) return rawV1(-v[1]) end -- lua only support same type operator compare, table compare with number is not allowed function V1MT.__eq(lhs, rhs) return lhs[1] == rhs[1] end -- lua only support same type operator compare, table compare with number is not allowed function V1MT.__lt(lhs, rhs) return lhs[1] < rhs[1] end -- lua only support same type operator compare, table compare with number is not allowed function V1MT.__le(lhs, rhs) return lhs[1] <= rhs[1] end function V1MT.__tostring(v) return string_format('%.3f', (v[1] / RawValueScaler)) end -- API:vector1 function V1MT.Rawset(self, raw) self[1] = raw return self end function V1MT.Reset(self, target) if target then --one argument maybe vector1 or number if getmetatable(target) == V1MT then self[1] = target[1] else self[1] = math_floor(target * RawValueScaler + 0.5) end else --no argument just reset self[1] = 0 end return self end function V1MT.Negative(self) self[1] = -self[1] return self end function V1MT.Add(self, target) if getmetatable(target) == V1MT then self[1] = self[1] + target[1] else self[1] = self[1] + math_floor(RawValueScaler * target + 0.5) end return self end function V1MT.Sub(self, target) if getmetatable(target) == V1MT then self[1] = self[1] - target[1] else self[1] = self[1] - math_floor(RawValueScaler * target + 0.5) end return self end function V1MT.Mul(self, target) local scaler = nil if getmetatable(target) == V1MT then scaler = target[1] / RawValueScaler else scaler = target end self[1] = math_floor(self[1] * scaler + 0.5) return self end function V1MT.Div(self, target) local scaler = nil if getmetatable(target) == V1MT then scaler = target[1] / RawValueScaler else scaler = target end self[1] = math_floor(self[1] / scaler + 0.5) return self end function V1MT.Mod(self, target) if getmetatable(target) == V1MT then self[1] = self[1] % target[1] else local t1 = math_floor(target * RawValueScaler + 0.5) self[1] = self[1] % t1 end return self end function V1MT.Clamp(self, min, max) if self[1] < min[1] then self[1] = min[1] elseif self[1] > max[1] then self[1] = max[1] end end function V1MT.Square(self) local v = self[1] / RawValueScaler return newV1(v * v) end function V1MT.Abs(self) if self < FP.Const.Zero then self:Mul(-1) end return self end FP.newV1 = newV1 FP.rawV1 = rawV1 FP.tonumberV1 = tonumberV1 V1MT.cloneV1 = cloneV1 V1MT.tonumberV1 = tonumberV1 --API: vector2 local V2MT = {} V2MT.__index = V2MT V2MT.__newindex = function(t, k, v) error('newindex on fixedpoint V2 '..k) end local function rawV2(rx, rz) local v2 = nil if V2InPoolCount > 0 then v2 = V2Pool[V2InPoolCount] v2.x[1], v2.z[1] = rx, rz V2Pool[V2InPoolCount] = nil V2InPoolCount = V2InPoolCount - 1 else local vx = setmetatable({rx}, V1MT) local vz = setmetatable({rz}, V1MT) v2 = {x = vx, z = vz} end return setmetatable(v2, V2MT) end local function newV2(nx, nz) local rx = getmetatable(nx) == V1MT and nx[1] or math_floor(RawValueScaler * nx + 0.5) local rz = getmetatable(nz) == V1MT and nz[1] or math_floor(RawValueScaler * nz + 0.5) local v2 = nil if V2InPoolCount > 0 then v2 = V2Pool[V2InPoolCount] v2.x[1], v2.z[1] = rx, rz V2Pool[V2InPoolCount] = nil V2InPoolCount = V2InPoolCount - 1 else local vx = setmetatable({rx}, V1MT) local vz = setmetatable({rz}, V1MT) v2 = {x = vx, z = vz} end return setmetatable(v2, V2MT) end local function cloneV2(self) local rx, rz = self.x[1], self.z[1] local v2 = nil if V2InPoolCount > 0 then v2 = V2Pool[V2InPoolCount] v2.x[1], v2.z[1] = rx, rz V2Pool[V2InPoolCount] = nil V2InPoolCount = V2InPoolCount - 1 else local vx = setmetatable({rx}, V1MT) local vz = setmetatable({rz}, V1MT) v2 = {x = vx, z = vz} end return setmetatable(v2, V2MT) end local function tonumberV2(self) return self.x[1] / RawValueScaler, self.z[1] / RawValueScaler end function V2MT.__add(lhs, rhs) local rx = lhs.x[1] + rhs.x[1] local rz = lhs.z[1] + rhs.z[1] return rawV2(rx, rz) end function V2MT.__sub(lhs, rhs) local rx = lhs.x[1] - rhs.x[1] local rz = lhs.z[1] - rhs.z[1] return rawV2(rx, rz) end function V2MT.__mul(lhs, rhs) local xMT, yMT = getmetatable(lhs), getmetatable(rhs) --x or y can has only one V2, the other is V1 or number if xMT == V2MT then if yMT == V1MT then --x is V2 and y is V1 rhs = rhs[1] / RawValueScaler else --x is V2 and y is number end local rx = math_floor(lhs.x[1] * rhs + 0.5) local rz = math_floor(lhs.z[1] * rhs + 0.5) return rawV2(rx, rz) elseif yMT == V2MT then if xMT == V1MT then --x is V1 and y is V2 lhs = lhs[1] / RawValueScaler else --x is number and y is V2 end local rx = math_floor(lhs * rhs.x[1] + 0.5) local rz = math_floor(lhs * rhs.z[1] + 0.5) return rawV2(rx, rz) else error('invalid arguments for __mul') end end function V2MT.__div(lhs, rhs) local xMT, yMT = getmetatable(lhs), getmetatable(rhs) --x or y can has only one V2, the other is V1 or number if xMT == V2MT then if yMT == V1MT then --x is V2 and y is V1 rhs = rhs[1] / RawValueScaler else --x is V2 and y is number end local rx = math_floor(lhs.x[1] / rhs + 0.5) local rz = math_floor(lhs.z[1] / rhs + 0.5) return rawV2(rx, rz) else error('invalid arguments for __div') end end function V2MT.__unm(v) local rx = -v.x[1] local rz = -v.z[1] return rawV2(rx, rz) end function V2MT.__eq(lhs, rhs) return lhs.x[1] == rhs.x[1] and lhs.z[1] == rhs.z[1] end function V2MT.__tostring(v) return string_format('(%.3f, %.3f)', (v.x[1] / RawValueScaler), (v.z[1] / RawValueScaler)) end function V2MT.Rawset(self, rawX, rawZ) self.x[1] = rawX self.z[1] = rawZ return self end --param1 with Vector2 type or param1 and param2 with number type function V2MT.Reset(self, param1, param2) if param1 then if param2 then --two arguments must be two numbers, skip V1MT check on param to speed up function call self.x[1] = math_floor(param1 * RawValueScaler + 0.5) self.z[1] = math_floor(param2 * RawValueScaler + 0.5) else --one argument must be vector2 self.x[1] = param1.x[1] self.z[1] = param1.z[1] end else --no argument just reset self.x[1] = 0 self.z[1] = 0 end return self end function V2MT.Negative(self) self.x[1] = -self.x[1] self.z[1] = -self.z[1] return self end function V2MT.Add(self, target) local sx, sz = self.x, self.z local tx, tz = target.x, target.z sx[1] = sx[1] + tx[1] sz[1] = sz[1] + tz[1] return self end function V2MT.Sub(self, target) local sx, sz = self.x, self.z local tx, tz = target.x, target.z sx[1] = sx[1] - tx[1] sz[1] = sz[1] - tz[1] return self end function V2MT.Mul(self, target) local sx, sz = self.x, self.z local scaler = nil if getmetatable(target) == V1MT then scaler = target[1] / RawValueScaler else scaler = target end sx[1] = math_floor(sx[1] * scaler + 0.5) sz[1] = math_floor(sz[1] * scaler + 0.5) return self end function V2MT.Div(self, target) local sx, sz = self.x, self.z local scaler = nil if getmetatable(target) == V1MT then scaler = target[1] / RawValueScaler else scaler = target end sx[1] = math_floor(sx[1] / scaler + 0.5) sz[1] = math_floor(sz[1] / scaler + 0.5) return self end function V2MT.Clamp(self, min, max) self.x:Clamp(min.x, max.x) self.z:Clamp(min.z, max.z) end function FP.V2.Less(self, target) return self.x[1] < target.x[1], self.z[1] < target.z[1] end function FP.V2.LessEquals(self, target) return self.x[1] <= target.x[1], self.z[1] <= target.z[1] end function FP.V2.Greater(self, target) return self.x[1] > target.x[1], self.z[1] > target.z[1] end function FP.V2.GreaterEquals(self, target) return self.x[1] >= target.x[1], self.z[1] >= target.z[1] end function FP.V2.SqrMagnitude(self ,outV1) local nx, nz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler if outV1 then outV1:Reset(nx * nx + nz * nz) else return newV1(nx * nx + nz * nz) end end function FP.V2.Magnitude(self, outV1) local nx, nz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler if outV1 then outV1:Reset(math_sqrt(nx * nx + nz * nz)) else return newV1(math_sqrt(nx * nx + nz * nz)) end end function FP.V2.DistanceSqr(self, target, outV1) local nx = (self.x[1] - target.x[1]) / RawValueScaler local nz = (self.z[1] - target.z[1]) / RawValueScaler if outV1 then outV1:Reset(nx * nx + nz * nz) else return newV1(nx * nx + nz * nz) end end function FP.V2.Distance(self, target) local nx = (self.x[1] - target.x[1]) / RawValueScaler local nz = (self.z[1] - target.z[1]) / RawValueScaler return newV1(math_sqrt(nx * nx + nz * nz)) end local _tempV1_1 = newV1(0) local _tempV1_2 = newV1(0) local _tempV1_3 = newV1(0) --点到直线的距离的平方 --dir是经过原点的直线,如果直线不经过原点,则需要转换坐标系把直线转换为经过原点的直线,同时pos也要转换到同一坐标系下 function FP.V2.DistanceSqrToDir(pos, dir, outV1) _tempV1_1:Reset(dir.x):Mul(dir.x) _tempV1_2:Reset(dir.z):Mul(dir.z) _tempV1_3:Reset(_tempV1_1):Add(_tempV1_2) _tempV1_1:Reset(dir.z):Mul(dir.z):Mul(pos.x):Mul(pos.x) --_tempV1最终值为点到直线距离的平方 _tempV1_2:Reset(-2):Mul(dir.x):Mul(dir.z):Mul(pos.x):Mul(pos.z) _tempV1_1:Add(_tempV1_2) _tempV1_2:Reset(dir.x):Mul(dir.x):Mul(pos.z):Mul(pos.z) _tempV1_1:Add(_tempV1_2):Div(_tempV1_3) local dist = outV1 or FP.newV1(0) dist:Reset(_tempV1_1) return dist end function FP.V2.IsDistanceSqrGreater(self, target, distSqr) local sx, sz = self.x, self.z local tx, tz = target.x, target.z local nx, nz = (sx[1] - tx[1]) / RawValueScaler, (sz[1] - tz[1]) / RawValueScaler local valueSqr = nx * nx + nz * nz if getmetatable(distSqr) == V1MT then distSqr = distSqr[1] / RawValueScaler end return valueSqr > distSqr end function FP.V2.IsDistanceSqrGreaterEquals(self, target, distSqr) local sx, sz = self.x, self.z local tx, tz = target.x, target.z local nx, nz = (sx[1] - tx[1]) / RawValueScaler, (sz[1] - tz[1]) / RawValueScaler local valueSqr = nx * nx + nz * nz if getmetatable(distSqr) == V1MT then distSqr = distSqr[1] / RawValueScaler end return valueSqr >= distSqr end function FP.V2.IsProjectionGreater(self, target, dist) local sx, sz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler local tx, tz = target.x[1] / RawValueScaler, target.z[1] / RawValueScaler local value = sx * tx + sz * tz if getmetatable(dist) == V1MT then dist = dist[1] / RawValueScaler end return math_abs(value) > dist end function FP.V2.IsProjectionGreaterHalf(self, target, dist) local sx, sz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler local tx, tz = target.x[1] / RawValueScaler, target.z[1] / RawValueScaler local value = sx * tx + sz * tz if getmetatable(dist) == V1MT then dist = dist[1] / RawValueScaler end return 2 * math_abs(value) > dist end function FP.V2.IsInRectRange(self, rectPos, rectDir, length, width) if rectDir == FP.Const.Vector2_Zero then return false end local selfDx, selfDz = (self.x[1] - rectPos.x[1]) / RawValueScaler, (self.z[1] - rectPos.z[1]) / RawValueScaler local rectDx, rectDz = rectDir.x[1] / RawValueScaler, rectDir.z[1] / RawValueScaler local rectRx, rectRz = rectDz, -rectDx length = getmetatable(length) == V1MT and length[1] / RawValueScaler or length width = getmetatable(width) == V1MT and width[1] / RawValueScaler or width return not (2 * math_abs(selfDx * rectDx + selfDz * rectDz) > width or 2 * math_abs(selfDx * rectRx + selfDz * rectRz) > length) end function FP.V2.IsDistanceGreater(self, target, dist) local sx, sz = self.x, self.z local tx, tz = target.x, target.z local nx, nz = (sx[1] - tx[1]) / RawValueScaler, (sz[1] - tz[1]) / RawValueScaler local valueSqr = nx * nx + nz * nz if getmetatable(dist) == V1MT then dist = dist[1] / RawValueScaler end return valueSqr > dist * dist end function FP.V2.IsDistanceGreaterEquals(self, target, dist) local sx, sz = self.x, self.z local tx, tz = target.x, target.z local nx, nz = (sx[1] - tx[1]) / RawValueScaler, (sz[1] - tz[1]) / RawValueScaler local valueSqr = nx * nx + nz * nz if getmetatable(dist) == V1MT then dist = dist[1] / RawValueScaler end return valueSqr >= dist * dist end function FP.V2.Normalized(self) local result = cloneV2(self) result:Normalize() return result end function FP.V2.Normalize(self) local nx, nz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler local mag = nx * nx + nz * nz if mag > 0 then mag = math_sqrt(mag) self.x[1] = math_floor(nx / mag * RawValueScaler + 0.5) self.z[1] = math_floor(nz / mag * RawValueScaler + 0.5) end end function FP.V2.Dot(self, target) local sx, sz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler local tx, tz = target.x[1] / RawValueScaler, target.z[1] / RawValueScaler return newV1(sx * tx + sz * tz) end function FP.V2.Cross(self, target) local sx, sz = self.x[1] / RawValueScaler, self.z[1] / RawValueScaler local tx, tz = target.x[1] / RawValueScaler, target.z[1] / RawValueScaler return newV1(sz * tx - sx * tz) -- 这个没有问题 ,这是用的unity的左手坐标系算的 ;别用标准的2D坐标系,那是右手坐标系 end --pointX和pointZ要求传定点数 --polygonPoints是多边形的顶点,要求必须按照相邻顺序,不要求是否顺时针或逆时针 function FP.V2.IsPointInPolygon(pointX,pointZ,polygonPoints) local ncross = 0 local sideCount = #polygonPoints for i=1,sideCount do local p1 = polygonPoints[i] local p2 = polygonPoints[i == sideCount and 1 or (i + 1)] if p1.z[1] ~= p2.z[1] then if pointZ[1] >= p1.z[1] or pointZ[1] >= p2.z[1] then if pointZ[1] < p1.z[1] or pointZ[1] < p2.z[1] then -- --old↓ 这块调用很多,性能有深度优化的必要 -- _tempV1_1:Reset(pointZ):Sub(p1.z) -- _tempV1_2:Reset(p2.x):Sub(p1.x) -- _tempV1_1:Mul(_tempV1_2) -- _tempV1_2:Reset(p2.z):Sub(p1.z) -- _tempV1_1:Div(_tempV1_2):Add(p1.x) -- old↑ --new↓ 全部展开,代码性能更好 _tempV1_1[1] = pointZ[1] _tempV1_1[1] = _tempV1_1[1] - p1.z[1] _tempV1_2[1] = p2.x[1] _tempV1_2[1] = _tempV1_2[1] - p1.x[1] _tempV1_1[1] = _tempV1_2[1] / RawValueScaler *_tempV1_1[1] --这里顺序跟 V1MT.Mul 里的计算顺序保持完全一样,避免不一致 _tempV1_2[1] = p2.z[1] _tempV1_2[1] = _tempV1_2[1] - p1.z[1] _tempV1_1[1] = _tempV1_1[1] / (_tempV1_2[1] / RawValueScaler) --这里顺序跟 V1MT.Div 里的计算顺序保持完全一样,避免不一致 _tempV1_1[1] = _tempV1_1[1] + p1.x[1] --new↑ if _tempV1_1[1] > pointX[1] then ncross = ncross + 1 end end end end end return ncross % 2 == 1 end local _tempV2 = newV2(0,0) local _rectCenter = newV2(0,0) local _centerToPointVec = newV2(0,0) --offsetX : 矩形区域在单位的right方向上的偏移 --offsetZ :矩形区域在单位的forward方向上的偏移 function FP.V2.IsPointInRect(point,rectCenter,rectForward,width,height,offsetX,offsetZ) local result = false local right = _tempV2 if rectForward.x ~= FP.Const.Zero then _tempV1_1:Reset(rectForward.z) _tempV1_1:Div(rectForward.x) _tempV1_1:Negative() right:Reset(FP.tonumberV1(_tempV1_1),1) else _tempV1_1:Reset(rectForward.x) _tempV1_1:Div(rectForward.z) _tempV1_1:Negative() right:Reset(1,FP.tonumberV1(_tempV1_1)) end right:Normalize() _rectCenter:Reset(rectCenter) if (offsetX and offsetX ~= 0) or (offsetZ and offsetZ ~= 0) then _rectCenter:Add(right * offsetX) _rectCenter:Add(rectForward * offsetZ) end _centerToPointVec:Reset(point) _centerToPointVec:Sub(_rectCenter) if _centerToPointVec ~= FP.Const.Vector2_Zero then local x, z = _centerToPointVec.x[1]/RawValueScaler, _centerToPointVec.z[1]/RawValueScaler local vDistance = x *rectForward.x[1]/RawValueScaler + z*rectForward.z[1]/RawValueScaler local hDistance = x *right.x[1]/RawValueScaler + z*right.z[1]/RawValueScaler result = math_abs(vDistance)*2 <= height and math_abs(hDistance)*2 <= width else result = true end return result end function FP.V2.IsRotateClockwise(self, target) return FP.Greater(FP.V2.Cross(self, target), 0) end --返回point绕着原点逆时针旋转rad弧度后的点 function FP.V2.RotateAroundOrigin(point,rad) return FP.V2.RotateAroundOriginXZ(point.x,point.z,rad) end --返回(x,z)绕着原点逆时针旋转rad弧度后的点 function FP.V2.RotateAroundOriginXZ(x1,z1,rad) if rad == FP.Const.Zero or rad == FP.Const.PI2 then return FP.newV2(x1,z1) else local sinValue = FP.Math.Sin(rad) local cosValue = FP.Math.Cos(rad) local x2 = x1 * cosValue - z1 * sinValue local y2 = z1 * cosValue + x1 * sinValue return FP.newV2(x2,y2) end end --返回0-180的定点数 function FP.V2.Angle(self, target, outV1) local result = outV1 or rawV1(0) local sx, sz = self.x, self.z local rsx, rsz = sx[1], sz[1] local tx, tz = target.x, target.z local rtx, rtz = tx[1], tz[1] if (rsx == 0 and rsz == 0) or (rtx == 0 and rtz == 0) then return result elseif rsx == rtx and rsz == rtz then return result else local x1 = rsx / RawValueScaler local z1 = rsz / RawValueScaler local x2 = rtx / RawValueScaler local z2 = rtz / RawValueScaler local dot = x1 * x2 + z1 * z2 local modMul = math_sqrt(x1 * x1 + z1 * z1) * math_sqrt(x2 * x2 + z2 * z2) local cosValue = dot / modMul if cosValue > 1 then result:Reset(0) elseif cosValue < -1 then result:Reset(Degree180) else result:Reset(math_acos(cosValue) * Rad2Deg) end end return result end --返回0-360的定点数 function FP.V2.Angle360(self, target, outV1) local result = FP.V2.Angle(self, target, outV1) if FP.V2.IsRotateClockwise(self, target) then result:Add(-360):Mul(-1) end return result end --返回0-360的定点数 function FP.V2.AngleToRight360(dir) local angle = FP.V2.Angle(FP.Const.Vector2_Right, dir) if dir.z < FP.Const.Zero then angle:Mul(-1) angle:Add(360) end return angle end --min、max都得是FP,指定角度 --正前方是0, 逆时针旋转角度增长 function FP.V2.AngleRangeCheckByDir(startDir, endDir, min, max) local angle = FP.V2.Angle(startDir, endDir) if FP.V2.IsRotateClockwise(startDir, endDir) then angle:Add(-360):Mul(-1) end return (angle >= min and angle <= max) end --center是扇形所属圆的圆心 --dir是扇形的中心线的方向 --angle是扇形的角度,角度制,必须传FP --radius是扇形所属圆的半径,如果传nil表示无限长,必须传FP function FP.V2.IsInFanshape(pos, center, dir, angle, radius) _tempV1_1:Reset(angle):Div(2) local halfAngle = _tempV1_1 _tempV1_2:Reset(360):Sub(halfAngle) local bigSideAngle = _tempV1_2 _tempV2:Reset(pos):Sub(center):Normalize() if FP.V2.AngleRangeCheckByDir(dir, _tempV2, FP.Const.Zero, halfAngle) or FP.V2.AngleRangeCheckByDir(dir, _tempV2, bigSideAngle, FP.Const.Num360) then if radius then _tempV1_3:Reset(radius):Mul(radius) --距离的平方 return not FP.V2.IsDistanceSqrGreater(pos, center, _tempV1_3) else --不传radius代表无限远,此时该方法仅判断角度 return true end else return false end end function FP.V2.GetRightByForward(self) local rx, rz = self.x[1], self.z[1] return rawV2(rz, -rx) end function FP.V2.GetOffsetToForward(forward, offset ,outV2) local fx, fz = tonumberV2(forward) local ox, oz = tonumberV2(offset) local rx = fz * ox + fx * oz local rz = -fx * ox + fz * oz if outV2 then outV2:Reset(rx,rz) else return newV2(rx, rz) end end function FP.V2.GetOffsetToAngle(angle, from) local radian = nil if getmetatable(angle) == V1MT then radian = angle[1] / RawValueScaler * Deg2Rad else radian = angle * Deg2Rad end local sin = math_sin(radian) local cos = math_cos(radian) local rx, rz = from.x[1], from.z[1] local nx = cos * rx + sin * rz local nz = -sin * rx + cos * rz return rawV2(nx, nz) end FP.newV2 = newV2 FP.tonumberV2 = tonumberV2 V2MT.tonumberV2 = tonumberV2 V2MT.cloneV2 = cloneV2 V2MT.SqrMagnitude = FP.V2.SqrMagnitude V2MT.Magnitude = FP.V2.Magnitude V2MT.DistanceSqr = FP.V2.DistanceSqr V2MT.Distance = FP.V2.Distance V2MT.Normalized = FP.V2.Normalized V2MT.Normalize = FP.V2.Normalize V2MT.Dot = FP.V2.Dot V2MT.Cross = FP.V2.Cross V2MT.Angle = FP.V2.Angle --const values FP.Const = { Max = rawV1(0x7FFFFFFF), Min = rawV1(0x80000000), Zero = newV1(0), Half = newV1(0.5), Fifth = newV1(0.2), Tenth = newV1(0.1), One = newV1(1), Two = newV1(2), Three = newV1(3), Four = newV1(4), Five = newV1(5), Six = newV1(6), Seven = newV1(7), Eight = newV1(8), Num360 = newV1(360), NegOne = newV1(-1), NegTwo = newV1(-2), NegHalf = newV1(-0.5), PI = newV1(PI), PI2 = newV1(PI * 2), PI_HALF = newV1(PI * 0.5), PI_FOURTH = newV1(PI * 0.25), Degree180 = newV1(Degree180), Degree360 = newV1(Degree180 * 2), Deg2Rad = newV1(PI / 180), --最好不要用这个值,精度太差了。最好用 FP.Math.Radian 来转换角度到弧度,精度更高 Vector2_Zero = newV2(0, 0), Vector2_One = newV2(1, 1), Vector2_Half = newV2(0.5, 0.5), Vector2_Forward = newV2(0, 1), Vector2_Right = newV2(1,0), } --API: math function FP.releaseV2(v) if V2InPoolCount < V2PoolCapacity then V2InPoolCount = V2InPoolCount + 1 V2Pool[V2InPoolCount] = setmetatable(v, V2InPoolMT) else print('fixedpoint v2 pool is full') end end function FP.GetV2PoolUsage() return V2InPoolCount, V2PoolCapacity end function FP.Equals(v, num) if getmetatable(v) == V1MT then return v[1] == math_floor(RawValueScaler * num + 0.5) else return v == num end end function FP.Less(v, num) if getmetatable(v) == V1MT then return v[1] < math_floor(RawValueScaler * num + 0.5) else return v < num end end function FP.LessEquals(v, num) if getmetatable(v) == V1MT then return v[1] <= math_floor(RawValueScaler * num + 0.5) else return v <= num end end function FP.Greater(v, num) if getmetatable(v) == V1MT then return v[1] > math_floor(RawValueScaler * num + 0.5) else return v > num end end function FP.GreaterEquals(v, num) if getmetatable(v) == V1MT then return v[1] >= math_floor(RawValueScaler * num + 0.5) else return v >= num end end local _tempV1_angle = newV1(0) function FP.Math.Sin(rad) local TriangleMath = require"Common/TriangleMath" _tempV1_angle:Reset(rad):Mul(180):Div(3.141) local raw = _tempV1_angle[1] / RawValueScaler return TriangleMath.Sin(raw):cloneV1() end function FP.Math.Cos(rad) local TriangleMath = require"Common/TriangleMath" _tempV1_angle:Reset(rad):Mul(180):Div(3.141) local raw = _tempV1_angle[1] / RawValueScaler return TriangleMath.Cos(raw):cloneV1() end function FP.Math.Max(lhs, rhs) if lhs[1] > rhs[1] then return rawV1(lhs[1]) else return rawV1(rhs[1]) end end function FP.Math.Min(lhs, rhs) if lhs[1] > rhs[1] then return rawV1(rhs[1]) else return rawV1(lhs[1]) end end function FP.Math.Floor(v) local result = rawV1(math_floor(v[1] / RawValueScaler) * RawValueScaler) return result end function FP.Math.Ceil(v) local result = rawV1(math_ceil(v[1] / RawValueScaler) * RawValueScaler) return result end function FP.Math.Sqrt(v) local num = v[1] / RawValueScaler return newV1(math_sqrt(num)) end function FP.Math.Abs(v) local raw = v[1] if raw < 0 then raw = -raw end return rawV1(raw) end function FP.Math.Degree(rad) return trimV1(rad[1] * Rad2Deg) end function FP.Math.Radian(deg) return trimV1(deg[1] * Deg2Rad) end -- 获取阵列中指定index的位置 -- index : 阵列中的序号, index=1的位置位于阵列的左前方(朝着阵列方向时的),index增加时位置往右直到此行已满再换行 -- center : 阵列的中心位置 -- maxColumn : 阵列的列数 -- maxRow : 阵列的行数 -- strideColumn : 阵列的列间距 -- strideRow : 阵列的行间距 -- dir : 阵列的方向,如果传nil默认阵列朝x轴正方向 function FP.Math.GetPhalanxPos(index, center, maxColumn, maxRow, strideColumn, strideRow, dir) local column = (index - 1) % maxColumn + 1 local row = math.floor((index - 1) / maxColumn) + 1 --队列排列方式:index从小到大,是从dir方向的左上(x、z最大的坐标)开始排,优先排满行再换行 local startLeft = FP.newV1(0) --左边起始偏移, 是当dir指向x正方向时,row 1的x坐标 startLeft:Reset(maxRow):Sub(1):Div(2):Mul(strideRow) --(maxRow - 1) / 2 * strideRow local startTop = FP.newV1(0) --顶部起始偏移, 是当dir指向x正方向时, column 1 的z坐标 startTop:Reset(maxColumn):Sub(1):Div(2):Mul(strideColumn) --(maxColumn - 1) / 2 * strideColumn local xOffset = FP.newV1(row) xOffset:Sub(1):Mul(strideRow):Mul(-1):Add(startLeft) local zOffset = FP.newV1(column) zOffset:Sub(1):Mul(strideColumn):Mul(-1):Add(startTop) local offset = FP.newV2(xOffset, zOffset) if dir then local dirRad = FP.V2.AngleToRight360(dir) dirRad:Mul(FP.Const.Deg2Rad) offset = FP.V2.RotateAroundOrigin(offset, dirRad) end return center + offset end local trackerFunc = { newV1 = newV1, rawV1 = rawV1, cloneV1 = cloneV1, newV2 = newV2, rawV2 = rawV2, cloneV2 = cloneV2, } local trackerInfo = {} local getinfo = debug.getinfo local traceback = debug.traceback local function hook(action) if action == 'call' then local info = getinfo(2, 'f') local func = info.func local tracker = trackerInfo[func] if tracker then local stack = traceback('', 3) local calls = tracker.calls local count = calls[stack] if count then calls[stack] = count + 1 else calls[stack] = 1 end end end end function FP.ToggleHook(min) local isOn = debug.gethook() if isOn then debug.sethook(nil) FP.PrintSummary(min) else trackerInfo = {} for funcName, func in pairs(trackerFunc) do local info = getinfo(func) trackerInfo[func] = {funcName = funcName, funcSummary = funcName..info.source..':'..info.linedefined, calls = {}} end debug.sethook(hook, 'c') end end function FP.PrintSummary(min) min = min or 0 local sorted = {} for func, tracker in pairs(trackerInfo) do local funcName = tracker.funcName local funcSummary = tracker.funcSummary local calls = tracker.calls for stack, count in pairs(calls) do if count > min then table_insert(sorted, {funcName = funcName, funcSummary = funcSummary, call_count = count, stack = stack}) end end end table_sort(sorted, function(a, b) if a.funcName == b.funcName then if a.call_count == b.call_count then return a.stack < b.stack else return a.call_count < b.call_count end else return a.funcName < b.funcName end end) for _, item in ipairs(sorted) do print(item.funcSummary, string_format('calls %d', item.call_count), item.stack) end end --DEBUG use only return FP --[[ do local FP = require('Common/fixedpoint') local fp1 = FP.newV1(1.001) local fp2 = FP.newV1(2.208) print(tostring(fp1), '+', tostring(fp2), '=', tostring(fp1 + fp2)) print(tostring(fp1), '-', tostring(fp2), '=', tostring(fp1 - fp2)) print(tostring(fp1), '*', tostring(fp2), '=', tostring(fp1 * fp2)) print(tostring(fp1), '/', tostring(fp2), '=', tostring(fp1 / fp2)) local fp3 = FP.newV1(2.345) local fp4 = FP.newV1(2.345) print('negative of', tostring(fp1), '=', tostring(-fp1)) print(tostring(fp3), '<', tostring(fp4), tostring(fp3 < fp4)) print(tostring(fp3), '<=', tostring(fp4), tostring(fp3 <=fp4)) print(tostring(fp3), '==', tostring(fp4), tostring(fp3 == fp4)) fp4 = fp4 - FP.newV1(0.001) print(tostring(fp3), '<', tostring(fp4), tostring(fp3 < fp4)) print(tostring(fp3), '<=', tostring(fp4), tostring(fp3<=fp4)) print('TODO compare with number not support', tostring(fp3), '==', tostring(fp4), tostring(fp3 == fp4)) fp4 = FP.newV1(0.5) print(tostring(fp4), '+', 0.5, '=', tostring(fp4 + 0.5), tostring(0.5 + fp4)) print(tostring(fp4), '-', 0.5, '=', tostring(fp4 - 0.5), tostring(0.5 - fp4)) print(tostring(fp4), '*', 0.5, '=', tostring(fp4 * 0.5), tostring(0.5 * fp4)) print(tostring(fp4), '/', 0.5, '=', tostring(fp4 / 0.5), tostring(0.5 / fp4)) print('TODO compare with number not support', tostring(fp4), '==', 0.5, '=', tostring(fp4 == 0.5), tostring(0.5 == fp4)) --print('TODO compare with number not support', tostring(fp4), '<', 0.5, '=', tostring(fp4 < 0.5), tostring(0.5 < fp4)) --print('TODO compare with number not support', tostring(fp4), '<=', 0.5, '=', tostring(fp4 <= 0.5), tostring(0.5 <= fp4)) local fp5 = fp3 fp3[1] = 111 print('ref equals', fp5 == fp3, tostring(fp5), '==', tostring(fp3)) local fp6 = fp3:cloneV1() fp3[1] = 222 print('clone equals', fp6 == fp3, tostring(fp6), '==', tostring(fp3)) end do local FP = require('Common/fixedpoint') local v1 = FP.newV2(1.234, 5.678) local v2 = FP.newV2(2.112, 3.432) local f1 = FP.newV1(0.5) local f2 = 0.5 print(tostring(v1), '+', tostring(v2), '=', tostring(v1 + v2)) print(tostring(v1), '-', tostring(v2), '=', tostring(v1 - v2)) print(tostring(v1), '*', tostring(f1), '=', tostring(v1 * f1)) print(tostring(v2), '/', tostring(f1), '=', tostring(v2 / f1)) print(tostring(v1), '*', tostring(f2), '=', tostring(v1 * f2)) print(tostring(v2), '/', tostring(f2), '=', tostring(v2 / f2)) local v3 = FP.newV2(3.333, 6.666) local v4 = FP.newV2(3.333, 6.666) print('negative of', tostring(v3), '=', tostring(-v3)) print(tostring(v3), '==', tostring(v4), tostring(v3 == v4)) v4 = v4 - FP.newV2(0.001, 0.001) print(tostring(v3), '==', tostring(v4), tostring(v3 == v4)) local v5 = v3 v5.x[1] = 111 print('ref equals', v5 == v3, tostring(v5), '==', tostring(v3)) local v6 = v3:cloneV2() v6.x[1] = 222 print('clone equals', v6 == v3, tostring(v6), '==', tostring(v3)) local v7 = FP.newV2(f1, f2) print(tostring(v7)) end do local FP = require('Common/fixedpoint') local v1 = FP.newV2(1, 1) local v2 = FP.newV2(3, 4) local fp = FP.newV1(2) print(tostring(v1:SqrMagnitude()), tostring(FP.V2.SqrMagnitude(v1))) print(tostring(v1:Magnitude()), tostring(FP.V2.Magnitude(v1))) print(tostring(v1:DistanceSqr(v2)), tostring(FP.V2.DistanceSqr(v1, v2))) print(tostring(v1:Distance(v2)), tostring(FP.V2.Distance(v1, v2))) print(FP.V2.IsDistanceSqrGreater(v1, v2, fp)) print(tostring(v1:Normalized()), tostring(FP.V2.Normalized(v1))) v1:Normalize() FP.V2.Normalize(v2) print(tostring(v1), tostring(v2)) print(tostring(v1:Dot(v2)), tostring(FP.V2.Dot(v1, v2))) print(tostring(v1:Cross(v2)), tostring(FP.V2.Cross(v1, v2))) print(FP.V2.IsRotateClockwise(v1, v2)) print(FP.V2.Angle(v1, v2)) print(tostring(FP.V2.GetRightByForward(v1))) print(tostring(FP.V2.GetOffsetToForward(v1, v2))) print(tostring(FP.V2.GetOffsetToAngle(fp, v1))) end do local FP = require('Common/fixedpoint') local v1 = FP.newV1(3.141) local v2 = FP.newV1(-90) local v3 = FP.newV1(90) print(tostring(FP.Math.Sin(v1))) print(tostring(FP.Math.Cos(v1))) print(tostring(FP.Math.Max(v1, v2))) print(tostring(FP.Math.Min(v1, v2))) print(tostring(FP.Math.Floor(v1))) print(tostring(FP.Math.Ceil(v1))) print(tostring(FP.Math.Sqrt(v1))) print(tostring(FP.Math.Abs(v2))) print(tostring(FP.Math.Degree(v1))) print(tostring(FP.Math.Radian(v3))) end --]]