public new bool RayCast(Ray ray, float rayLength, out Vector3 hit, out ushort lineIndex, out int stopIndex, out int segmentIndex) { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; float num5 = 16f; float num6 = 9f; Vector3 vector = Vector3.zero; Vector3 vector2 = Vector3.zero; Vector3 origin = ray.origin; Vector3 normalized = ray.direction.normalized; Vector3 b = ray.origin + normalized * rayLength; Segment3 segment = new Segment3(origin, b); NetManager instance = Singleton <NetManager> .instance; for (int i = 1; i < 256; i++) { if ((this.m_lines.m_buffer[i].m_flags & (TransportLine.Flags.Created | TransportLine.Flags.Temporary)) == TransportLine.Flags.Created && this.m_lines.m_buffer[i].m_bounds.IntersectRay(ray)) { TransportManager.LineSegment[] array = this.m_lineSegments[i]; Bezier3[] array2 = this.m_lineCurves[i]; ushort stops = this.m_lines.m_buffer[i].m_stops; ushort num7 = stops; int num8 = 0; while (num7 != 0) { Vector3 position = instance.m_nodes.m_buffer[(int)num7].m_position; float num9 = Line3.DistanceSqr(ray.direction, ray.origin - position); if (num9 < num5) { num = i; num3 = num8; num5 = num9; vector = position; } if (array.Length > num8 && array[num8].m_bounds.IntersectRay(ray)) { int curveStart = array[num8].m_curveStart; int curveEnd = array[num8].m_curveEnd; for (int j = curveStart; j < curveEnd; j++) { Vector3 min = array2[j].Min() - new Vector3(3f, 3f, 3f); Vector3 max = array2[j].Max() + new Vector3(3f, 3f, 3f); Bounds bounds = default(Bounds); bounds.SetMinMax(min, max); if (bounds.IntersectRay(ray)) { float t; float num10; num9 = array2[j].DistanceSqr(segment, out t, out num10); if (num9 < num6) { num2 = i; num4 = num8; num6 = num9; vector2 = array2[j].Position(t); } } } } num7 = TransportLine.GetNextStop(num7); if (num7 == stops) { break; } if (++num8 >= 32768) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } } } if (num != 0) { hit = vector; lineIndex = (ushort)num; stopIndex = num3; segmentIndex = -1; return(true); } if (num2 != 0) { hit = vector2; lineIndex = (ushort)num2; stopIndex = -1; segmentIndex = num4; return(true); } hit = Vector3.zero; lineIndex = 0; stopIndex = -1; segmentIndex = -1; return(false); }
private void AreEqual_ClosestPoint(Line3 line, Vector3 point) { AreEqual_ClosestPoint(line, point, point); }
private void True_Intersect(Line3 line, Vector3 point) { Assert.True(Intersect.PointLine(point, line), format, line, point); }
private void AreEqual_Distance(Line3 line, Sphere sphere, float expected = 0) { AreEqual(Distance.LineSphere(line.origin, line.direction, sphere.center, sphere.radius), expected); }
public void CylinderSnapping() { // Get Boundary2 if (boundaryPoints_2d == null) { boundaryPoints_2d = GetBoundaryPoints(mark); } List <MyVector2> boundary2 = ExtractOutline(edgeImage, boundaryPoints_2d); // Project 2D edge points //topCircle = new MyCircle(topCircle.Center, topCircle.Radius, -topCircle.Normal); MyVector3 normal = topCircle.Normal.Cross(this.camera.target).Cross(topCircle.Normal); MyPlane sectionPlane = new MyPlane(topCircle.Center, normal); boundary3 = Proj2dToPlane(sectionPlane, boundary2); topCircle = CiriFixTopCircle(topCircle, boundary3); // UpdateCircleNormal // foreach (var pbondary3 in pbondary3) // { // } // if (topCircle.Center) //{ //} // Algorithm Init Params double offset = topCircle.Radius / 50; cur_p = topCircle.Center - offset * topCircle.Normal; cur_dire = 1.0 * topCircle.Normal; MyVector3 cur_dire_new = new MyVector3(cur_dire); MyVector3 cur_p_new = new MyVector3(-1 * cur_p); Insection1 = new MyVector3(1, 1, 1); Insection2 = new MyVector3(0, 0, 0); int norInsec = -1; int notNorInsec = -1; MyVector3 tangential1 = new MyVector3(1, 1, 1); MyVector3 tangential2 = new MyVector3(1, 1, 1); List <MyCircle> CircleLists = new List <MyCircle>(); CircleLists.Add(topCircle); // Fix first circle int iter = 0; double r = double.MaxValue; System.Console.WriteLine(Insection1.Dot(tangential2)); System.Console.WriteLine(Math.Cos(2.0 / 3.0 * Math.PI)); int MaxInter = 1000; GeneratedCenters = new List <MyVector3>(); List <double> radius = new List <double>(); List <double> weights = new List <double>(); List <MyVector3> dires = new List <MyVector3>(); while (--MaxInter > 0) // { if (Insection1 == Insection2) // 交点一直保持相同 { System.Console.WriteLine("Warning: Insection is same!"); // 半径过小 break; } if (cur_dire.Dot(cur_dire_new) < 0) // 移动方向反向 { System.Console.WriteLine("Warning: Move Direction!"); break; } if (cur_p + offset * cur_dire == cur_p_new) // 中心点没有移动 { System.Console.WriteLine("Warning: Center not move!"); break; } RayTracein3DPlane(boundary3, cur_p_new, cur_dire_new.Cross(sectionPlane.Normal()), sectionPlane.Normal(), out norInsec, out notNorInsec); System.Console.WriteLine("{0} , {1}", MyVector3.Distance(boundary3[norInsec], cur_p_new), MyVector3.Distance(boundary3[notNorInsec], cur_p_new)); test1 = new Line3(boundary3[norInsec], cur_p_new - boundary3[norInsec]); test2 = new Line3(boundary3[notNorInsec], cur_p_new - boundary3[notNorInsec]); if (MyVector3.Distance(boundary3[norInsec], cur_p_new) < topCircle.Radius / 20 || // close to bottom MyVector3.Distance(boundary3[notNorInsec], cur_p_new) < topCircle.Radius / 20) { System.Console.WriteLine("Warning: Close to bottom!"); break; } if (tangential1.Dot(tangential2) < Math.Cos(2.0 / 3.0 * Math.PI)) //切线相向 { System.Console.WriteLine("Warning: tangential get oppsite direction!"); break; } if (r < 0.0001) { System.Console.WriteLine("Warning: Radius is too small!"); // 半径过小 break; } //if (MyVector3.Distance(cur_p, cur_p_new) ) //{ // System.Console.WriteLine("Warning: Radius is too small!"); // 半径过小 // break; //} if (iter != 0) { //offset = 1 / MyVector3.Distance(cur_p, cur_p_new) * 0.000001 + 0.5 * offset; offset = topCircle.Radius / 20; //System.Console.WriteLine("{0}", offset); cur_dire = cur_dire_new; cur_p = cur_p_new + offset * cur_dire; CircleLists.Add(new MyCircle(cur_p, r, cur_dire)); // Get Data for Fit double weight = Math.Abs(cur_dire_new.Dot(cur_dire)); GeneratedCenters.Add(cur_p_new); weights.Add(weight); radius.Add(r); dires.Add(cur_dire); } // Step1: Get IntersectionPoitn RayTracein3DPlane(boundary3, cur_p, cur_dire, sectionPlane.Normal(), out norInsec, out notNorInsec); // Step2 : Get Two Local Tangential Insection1 = boundary3[norInsec]; Insection2 = boundary3[notNorInsec]; tangential1 = GetLocalTangential(norInsec, boundary3, cur_dire); tangential2 = GetLocalTangential(notNorInsec, boundary3, cur_dire); // Visualization setdirecLine = new Line3(cur_p, cur_dire); setLine1 = new Line3(Insection1, tangential1); setLine2 = new Line3(Insection2, tangential2); // Step3 : Get New Cur Direction and Cur Point cur_dire_new = (tangential1 + tangential2) / 2; RayTracein3DPlane(boundary3, cur_p, cur_dire_new, sectionPlane.Normal(), out norInsec, out notNorInsec); cur_p_new = (boundary3[norInsec] + boundary3[notNorInsec]) / 2; r = 0.5 * MyVector3.Distance(boundary3[norInsec], boundary3[notNorInsec]); iter++; this.view.Refresh(); } // Fit centers and radius; GeneratedCenters = FittingCentersCurve(GeneratedCenters, weights); int inter = 1; while (inter-- > 0) { radius = FittRadius(radius); } // ReBuild Object CircleLists.Clear(); CircleLists.Add(topCircle); // Fix first circle for (int i = 0; i < GeneratedCenters.Count; i++) { CircleLists.Add(new MyCircle(GeneratedCenters[i], radius[i], dires[i])); } CurveCyliner = new SweepMesh(CircleLists); }
} // FillFromTwoLines private void FillFromThreeLines(CustomerAddressModel filled) { string uLine1 = Line1.ToUpper(); if (uLine1.StartsWith("APARTMENT") || uLine1.StartsWith("FLAT")) { filled.FlatOrApartmentNumber = Line1; filled.HouseNumber = Regex.Match(Line2, "\\d*").Value; if (!string.IsNullOrWhiteSpace(filled.HouseNumber)) { string[] line2 = Line2.Split(' '); filled.HouseNumber = line2[0]; filled.Address1 = string.Join(" ", line2.Skip(1)); filled.Address2 = Line3; } else { filled.HouseNumber = Regex.Match(Line3, "\\d*").Value; if (!string.IsNullOrWhiteSpace(filled.HouseNumber)) { string[] line3 = Line3.Split(' '); filled.HouseNumber = line3[0]; filled.Address1 = string.Join(" ", line3.Skip(1)); } else { filled.Address1 = Line3; } filled.HouseName = Line2; } // if return; } // if bool neitherUnitNorBlock = !uLine1.Contains("UNIT") && !uLine1.Contains("BLOCK") && Regex.Match(Line1, "^\\d[0-9a-zA-Z ]*$").Success; if (neitherUnitNorBlock) { filled.HouseNumber = Regex.Match(Line1, "\\d*").Value; if (string.IsNullOrWhiteSpace(filled.HouseNumber)) { return; } var line1 = Line1.Split(' '); filled.HouseNumber = line1[0]; filled.Address1 = string.Join(" ", line1.Skip(1)); filled.Address2 = Line2; } else { filled.HouseName = Line1; filled.Address1 = Line2; filled.Address2 = Line3; } // if } // FillFromThreeLines
public void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) { #if DEBUG bool debug = GlobalConfig.Instance.Debug.Switches[16] && GlobalConfig.Instance.Debug.NodeId == vehicleID; #endif ushort leadingVehicle = vehicleData.m_leadingVehicle; uint currentFrameIndex = Singleton <SimulationManager> .instance.m_currentFrameIndex; VehicleInfo leaderInfo; if (leaderID != vehicleID) { leaderInfo = leaderData.Info; } else { leaderInfo = this.m_info; } TramBaseAI tramBaseAI = leaderInfo.m_vehicleAI as TramBaseAI; if (leadingVehicle != 0) { frameData.m_position += frameData.m_velocity * 0.4f; } else { frameData.m_position += frameData.m_velocity * 0.5f; } frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f; Vector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f); Vector3 posAfterWheelRot = frameData.m_position + wheelBaseRot; Vector3 posBeforeWheelRot = frameData.m_position - wheelBaseRot; float acceleration = this.m_info.m_acceleration; float braking = this.m_info.m_braking; float curSpeed = frameData.m_velocity.magnitude; Vector3 afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot; float afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude; Quaternion curInvRot = Quaternion.Inverse(frameData.m_rotation); Vector3 curveTangent = curInvRot * frameData.m_velocity; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): ================================================"); Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): leadingVehicle={leadingVehicle} frameData.m_position={frameData.m_position} frameData.m_swayPosition={frameData.m_swayPosition} wheelBaseRot={wheelBaseRot} posAfterWheelRot={posAfterWheelRot} posBeforeWheelRot={posBeforeWheelRot} acceleration={acceleration} braking={braking} curSpeed={curSpeed} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} curInvRot={curInvRot} curveTangent={curveTangent} this.m_info.m_generatedInfo.m_wheelBase={this.m_info.m_generatedInfo.m_wheelBase}"); } #endif Vector3 forward = Vector3.forward; Vector3 targetMotion = Vector3.zero; float targetSpeed = 0f; float motionFactor = 0.5f; float turnAngle = 0f; if (leadingVehicle != 0) { VehicleManager vehMan = Singleton <VehicleManager> .instance; Vehicle.Frame leadingVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].GetLastFrameData(); VehicleInfo leadingVehInfo = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].Info; float attachOffset; if ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags) 0) { attachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f; } else { attachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f; } float leadingAttachOffset; if ((vehMan.m_vehicles.m_buffer[(int)leadingVehicle].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags) 0) { leadingAttachOffset = leadingVehInfo.m_attachOffsetFront - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f; } else { leadingAttachOffset = leadingVehInfo.m_attachOffsetBack - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f; } Vector3 curPosMinusRotAttachOffset = frameData.m_position - frameData.m_rotation * new Vector3(0f, 0f, attachOffset); Vector3 leadingPosPlusRotAttachOffset = leadingVehLastFrameData.m_position + leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingAttachOffset); wheelBaseRot = leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingVehInfo.m_generatedInfo.m_wheelBase * 0.5f); Vector3 leadingPosBeforeWheelRot = leadingVehLastFrameData.m_position - wheelBaseRot; if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags) 0) { int someIndex = -1; InvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f); afterRotToTargetPos1DiffSqrMag = 0f; } float attachRotDist = Mathf.Max(Vector3.Distance(curPosMinusRotAttachOffset, leadingPosPlusRotAttachOffset), 2f); float one = 1f; float attachRotSqrDist = attachRotDist * attachRotDist; float oneSqr = one * one; int i = 0; if (afterRotToTargetPos1DiffSqrMag < attachRotSqrDist) { if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags) 0) { InvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, 0, ref leaderData, ref i, 1, 2, attachRotSqrDist, oneSqr); } while (i < 4) { vehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1)); i++; } afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot; afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude; } afterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff; float negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + leadingVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + leadingAttachOffset); bool hasPath = false; if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags) 0) { float u1; float u2; if (Line3.Intersect(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, negTotalAttachLen, out u1, out u2)) { targetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f); } else { Line3.DistanceSqr(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, out u1); targetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f); } hasPath = true; } if (hasPath) { if (Vector3.Dot(leadingPosBeforeWheelRot - posAfterWheelRot, posAfterWheelRot - posBeforeWheelRot) < 0f) { motionFactor = 0f; } } else { float leadingPosBeforeToAfterWheelRotDist = Vector3.Distance(leadingPosBeforeWheelRot, posAfterWheelRot); motionFactor = 0f; targetMotion = curInvRot * ((leadingPosBeforeWheelRot - posAfterWheelRot) * (Mathf.Max(0f, leadingPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, leadingPosBeforeToAfterWheelRotDist * 0.6f))); } } else { float estimatedFrameDist = (curSpeed + acceleration) * (0.5f + 0.5f * (curSpeed + acceleration) / braking) + (this.m_info.m_generatedInfo.m_size.z - this.m_info.m_generatedInfo.m_wheelBase) * 0.5f; float maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f); float meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 2f); float maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd; float meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd; if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags) 0) { int someIndex = -1; InvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f); afterRotToTargetPos1DiffSqrMag = 0f; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): dot < 0"); } #endif } #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Leading vehicle is 0. vehicleData.m_targetPos0={vehicleData.m_targetPos0} vehicleData.m_targetPos1={vehicleData.m_targetPos1} posBeforeWheelRot={posBeforeWheelRot} posBeforeWheelRot={posAfterWheelRot} estimatedFrameDist={estimatedFrameDist} maxSpeedAdd={maxSpeedAdd} meanSpeedAdd={meanSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr} meanSpeedAddSqr={meanSpeedAddSqr} afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag}"); } #endif int posIndex = 0; bool hasValidPathTargetPos = false; if ((afterRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags) 0) { if (vehicleData.m_path != 0u) { InvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr); } if (posIndex < 4) { hasValidPathTargetPos = true; while (posIndex < 4) { vehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1)); posIndex++; } } afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot; afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude; } #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): posIndex={posIndex} hasValidPathTargetPos={hasValidPathTargetPos}"); } #endif if (leaderData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags) 0) { NetManager netMan = Singleton <NetManager> .instance; byte leaderPathPosIndex = leaderData.m_pathPositionIndex; byte leaderLastPathOffset = leaderData.m_lastPathOffset; if (leaderPathPosIndex == 255) { leaderPathPosIndex = 0; } int noise; float leaderLen = 1f + leaderData.CalculateTotalLength(leaderID, out noise); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): leaderPathPosIndex={leaderPathPosIndex} leaderLastPathOffset={leaderLastPathOffset} leaderPathPosIndex={leaderPathPosIndex} leaderLen={leaderLen}"); } #endif // reserve space / add traffic PathManager pathMan = Singleton <PathManager> .instance; PathUnit.Position pathPos; if (pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetPosition(leaderPathPosIndex >> 1, out pathPos)) { netMan.m_segments.m_buffer[(int)pathPos.m_segment].AddTraffic(Mathf.RoundToInt(leaderLen * 2.5f), noise); bool reservedSpaceOnCurrentLane = false; if ((leaderPathPosIndex & 1) == 0 || leaderLastPathOffset == 0) { uint laneId = PathManager.GetLaneID(pathPos); if (laneId != 0u) { Vector3 curPathOffsetPos = netMan.m_lanes.m_buffer[laneId].CalculatePosition((float)pathPos.m_offset * 0.003921569f); float speedAdd = 0.5f * curSpeed * curSpeed / this.m_info.m_braking; float afterWheelRotCurPathOffsetDist = Vector3.Distance(posAfterWheelRot, curPathOffsetPos); float beforeWheelRotCurPathOffsetDist = Vector3.Distance(posBeforeWheelRot, curPathOffsetPos); if (Mathf.Min(afterWheelRotCurPathOffsetDist, beforeWheelRotCurPathOffsetDist) >= speedAdd - 1f) { netMan.m_lanes.m_buffer[laneId].ReserveSpace(leaderLen); reservedSpaceOnCurrentLane = true; } } } if (!reservedSpaceOnCurrentLane && pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetNextPosition(leaderPathPosIndex >> 1, out pathPos)) { uint nextLaneId = PathManager.GetLaneID(pathPos); if (nextLaneId != 0u) { netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(leaderLen); } } } if ((ulong)(currentFrameIndex >> 4 & 15u) == (ulong)((long)(leaderID & 15))) { // check if vehicle can proceed to next path position bool canProceeed = false; uint curLeaderPathId = leaderData.m_path; int curLeaderPathPosIndex = leaderPathPosIndex >> 1; int k = 0; while (k < 5) { bool invalidPos; if (PathUnit.GetNextPosition(ref curLeaderPathId, ref curLeaderPathPosIndex, out pathPos, out invalidPos)) { uint laneId = PathManager.GetLaneID(pathPos); if (laneId != 0u && !netMan.m_lanes.m_buffer[laneId].CheckSpace(leaderLen)) { k++; continue; } } if (invalidPos) { this.InvalidPath(vehicleID, ref vehicleData, leaderID, ref leaderData); } canProceeed = true; break; } if (!canProceeed) { leaderData.m_flags |= Vehicle.Flags.Congestion; } } } float maxSpeed; if ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags) 0) { maxSpeed = 0f; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is stopped. maxSpeed={maxSpeed}"); } #endif } else { maxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData)); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped. maxSpeed={maxSpeed}"); } #endif } #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Start of second part. curSpeed={curSpeed} curInvRot={curInvRot}"); } #endif afterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} (old afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag})"); } #endif Vector3 zero = Vector3.zero; bool blocked = false; float forwardLen = 0f; if (afterRotToTargetPos1DiffSqrMag > 1f) // TODO why is this not recalculated? { forward = VectorUtils.NormalizeXZ(afterRotToTargetPos1Diff, out forwardLen); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > 1f. forward={forward} forwardLen={forwardLen}"); } #endif if (forwardLen > 1f) { Vector3 fwd = afterRotToTargetPos1Diff; maxSpeedAdd = Mathf.Max(curSpeed, 2f); maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): forwardLen > 1f. fwd={fwd} maxSpeedAdd={maxSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr}"); } #endif if (afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) { float fwdLimiter = maxSpeedAdd / Mathf.Sqrt(afterRotToTargetPos1DiffSqrMag); fwd.x *= fwdLimiter; fwd.y *= fwdLimiter; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr. afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} maxSpeedAddSqr={maxSpeedAddSqr} fwdLimiter={fwdLimiter} fwd={fwd}"); } #endif } if (fwd.z < -1f) // !!! { #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): fwd.z < -1f. fwd={fwd}"); } #endif if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags) 0) { Vector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0; if ((curInvRot * targetPos0TargetPos1Diff).z < -0.01f) // !!! { #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): (curInvRot * targetPos0TargetPos1Diff).z < -0.01f. curInvRot={curInvRot} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff}"); } #endif if (afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f) // !!! { #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. fwd={fwd} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff}"); } #endif /*fwd.z = 0f; * afterRotToTargetPos1Diff = Vector3.zero;*/ maxSpeed = 0.5f; // NON-STOCK CODE #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): (1) set maxSpeed={maxSpeed}"); } #endif } else { posAfterWheelRot = posBeforeWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase; posIndex = -1; InvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, vehicleData.m_targetPos1, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) + 1f, 1f); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z >= Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. Invoked UpdatePathTargetPositions. posAfterWheelRot={posAfterWheelRot} posBeforeWheelRot={posBeforeWheelRot} this.m_info.m_generatedInfo.m_wheelBase={this.m_info.m_generatedInfo.m_wheelBase}"); } #endif } } else { posIndex = -1; InvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f); vehicleData.m_targetPos1 = posAfterWheelRot; fwd.z = 0f; afterRotToTargetPos1Diff = Vector3.zero; maxSpeed = 0f; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is waiting for a path. posIndex={posIndex} vehicleData.m_targetPos1={vehicleData.m_targetPos1} fwd={fwd} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} maxSpeed={maxSpeed}"); } #endif } } motionFactor = 0f; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Reset motion factor. motionFactor={motionFactor}"); } #endif } forward = VectorUtils.NormalizeXZ(fwd, out forwardLen); float curve = Mathf.PI / 2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2 if (forwardLen > 1f) { curve /= forwardLen; } float targetDist = forwardLen; #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): targetDist={targetDist} fwd={fwd} curve={curve} maxSpeed={maxSpeed}"); } #endif if (vehicleData.m_targetPos1.w < 0.1f) { maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve); maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos1.w, braking * 0.9f)); } else { maxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve)); } #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [1] maxSpeed={maxSpeed}"); } #endif maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking * 0.9f)); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [2] maxSpeed={maxSpeed}"); } #endif targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1); maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking * 0.9f)); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [3] maxSpeed={maxSpeed}"); } #endif targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2); if (vehicleData.m_targetPos3.w < 0.01f) { targetDist = Mathf.Max(0f, targetDist + (this.m_info.m_generatedInfo.m_wheelBase - this.m_info.m_generatedInfo.m_size.z) * 0.5f); } maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking * 0.9f)); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [4] maxSpeed={maxSpeed}"); } #endif CarAI.CheckOtherVehicles(vehicleID, ref vehicleData, ref frameData, ref maxSpeed, ref blocked, ref zero, estimatedFrameDist, braking * 0.9f, lodPhysics); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): CheckOtherVehicles finished. blocked={blocked}"); } #endif if (maxSpeed < curSpeed) { float brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed)); targetSpeed = Mathf.Max(maxSpeed, curSpeed - brake); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed < curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} brake={brake} targetSpeed={targetSpeed}"); } #endif } else { float accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed)); targetSpeed = Mathf.Min(maxSpeed, curSpeed + accel); #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed >= curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} accel={accel} targetSpeed={targetSpeed}"); } #endif } } } else if (curSpeed < 0.1f && hasValidPathTargetPos && leaderInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) { leaderData.Unspawn(leaderID); return; } if ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags) 0 && maxSpeed < 0.1f) { #if DEBUG if (debug) { Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped but maxSpeed < 0.1. maxSpeed={maxSpeed}"); } #endif blocked = true; } if (blocked) { leaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255); } else { leaderData.m_blockCounter = 0; } if (forwardLen > 1f) { turnAngle = Mathf.Asin(forward.x) * Mathf.Sign(targetSpeed); targetMotion = forward * targetSpeed; } else { Vector3 vel = Vector3.ClampMagnitude(afterRotToTargetPos1Diff * 0.5f - curveTangent, braking); targetMotion = curveTangent + vel; } } bool mayBlink = (currentFrameIndex + (uint)leaderID & 16u) != 0u; Vector3 springs = targetMotion - curveTangent; Vector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion; Vector3 targetBeforeWheelRotMotion = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) * (targetMotion.magnitude * motionFactor); targetBeforeWheelRotMotion -= targetAfterWheelRotMotion * (Vector3.Dot(targetAfterWheelRotMotion, targetBeforeWheelRotMotion) / Mathf.Max(1f, targetAfterWheelRotMotion.sqrMagnitude)); posAfterWheelRot += targetAfterWheelRotMotion; posBeforeWheelRot += targetBeforeWheelRotMotion; frameData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot); Vector3 targetPos = posAfterWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f); frameData.m_velocity = targetPos - frameData.m_position; if (leadingVehicle != 0) { frameData.m_position += frameData.m_velocity * 0.6f; } else { frameData.m_position += frameData.m_velocity * 0.5f; } frameData.m_swayVelocity = frameData.m_swayVelocity * (1f - this.m_info.m_dampers) - springs * (1f - this.m_info.m_springs) - frameData.m_swayPosition * this.m_info.m_springs; frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f; frameData.m_steerAngle = 0f; frameData.m_travelDistance += targetMotion.z; if (leadingVehicle != 0) { frameData.m_lightIntensity = Singleton <VehicleManager> .instance.m_vehicles.m_buffer[(int)leaderID].GetLastFrameData().m_lightIntensity; } else { frameData.m_lightIntensity.x = 5f; frameData.m_lightIntensity.y = ((springs.z >= -0.1f) ? 0.5f : 5f); frameData.m_lightIntensity.z = ((turnAngle >= -0.1f || !mayBlink) ? 0f : 5f); frameData.m_lightIntensity.w = ((turnAngle <= 0.1f || !mayBlink) ? 0f : 5f); } frameData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags) 0); frameData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags) 0); //base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics); }
protected void DrawLine(ref Line3 line) { Gizmos.DrawLine(line.Center - line.Direction * _lineLength, line.Center + line.Direction * _lineLength); }
static void ClowWater() { List <Vector3> p3 = new List <Vector3>(); p3.Add(new Vector3(1.1, 0.9, 1.0)); p3.Add(new Vector3(6.9, 7.1, 7.0)); var line3fit = AutomationLibrary.Mathematics.Fitting.GeometricFits.FitLine(p3); var line3 = Line3.FromPointAndDirection(new Vector3(0.5, 0.5, 0.5), new Vector3(1, 1, 1)); var nearest = line3.GetClosestPoint(new Vector3(27, -1.4, 19)); List <Vector2> points = new List <Vector2>(); using (var reader = System.IO.File.OpenText(@"C:\Users\douglas\desktop\pipe.csv")) { reader.ReadLine(); // skip header while (true) { var line = reader.ReadLine(); if (line == null) { break; } var nums = line.Split(','); var values = nums.Select(n => double.Parse(n)).ToArray(); points.Add(new Vector2(values[0], values[1])); } } var ellipse = AutomationLibrary.Mathematics.Fitting.GeometricFits.FitEllipse(points); var ellipseFunc = AutomationLibrary.Mathematics.Curves.CircularFunction.FromCartesianPoints(ellipse.Center, points); var smoothEllipseFunc = ellipseFunc.SavitzkyGolaySmooth(3, 21); points.Clear(); points.AddRange(GeneratePointsOnEllipticalArc(new Vector2(0.37, -2.4), 21, 24.26, 96.3 * Math.PI / 180.0, .020, -95.0 * Math.PI / 180.0, 97.0 * Math.PI / 180.0).Take(1000)); var pointSet = new PointCloud2(points); var voronoi = AutomationLibrary.Mathematics.Geometry.Voronoi.VoronoiDiagram.ComputeForPoints(points); voronoi = voronoi.Filter(0); // build map of nearest points var centersOfInfiniteCells = new HashSet <Vector2>(); foreach (var edge in voronoi.Edges) { if (edge.IsPartlyInfinite) { centersOfInfiniteCells.Add(edge.LeftData); centersOfInfiniteCells.Add(edge.RightData); } } var pointSet2 = new PointCloud2(centersOfInfiniteCells); var mcc = pointSet2.ComputeMinimumCircumscribingCircle(); var mic = ComputeMaximumInscribedCircle(pointSet, voronoi, mcc); var lsc = GeometricFits.FitCircle(points); using (var writer = System.IO.File.CreateText(@"C:\users\douglas\desktop\circlepoints.csv")) { writer.WriteLine("X,Y"); foreach (var point in pointSet) { writer.WriteLine("{0},{1}", point.X, point.Y); } } Console.WriteLine("n = {0}", points.Count); Console.WriteLine("MIC @ ({0}), r = {1}", mic.Center, mic.Radius); Console.WriteLine("LSC @ ({0}), r = {1}", lsc.Center, lsc.Radius); Console.WriteLine("MCC @ ({0}), r = {1}", mcc.Center, mcc.Radius); Console.WriteLine(); Console.WriteLine("draw.circle({0}, {1}, {2}, border='{3}')", mic.Center.X, mic.Center.Y, mic.Radius, "red"); Console.WriteLine("draw.circle({0}, {1}, {2}, border='{3}')", lsc.Center.X, lsc.Center.Y, lsc.Radius, "blue"); Console.WriteLine("draw.circle({0}, {1}, {2}, border='{3}')", mcc.Center.X, mcc.Center.Y, mcc.Radius, "green"); Console.ReadLine(); }
public FieldTrigger(byte doorId, Line3 boundary) { DoorID = doorId; Boundary = boundary; }
// Token: 0x06000182 RID: 386 RVA: 0x000065CC File Offset: 0x000047CC public LineCurve3(Line3 line) { this.Origin = line.Origin; this.Direction = line.Direction; }
public Line3 UpdateLine3() { line3 = MathUtil.CreateLine3(LineObject); DrawLine3(); return(line3); }
/// <summary> /// Allows you to compare another address object to determine if the two addresses are the same. /// </summary> /// <param name="a2">Another address object.</param> /// <returns>If true, the current address matches the address in the parameter.</returns> public bool IsEqualTo(Address a2) { if (a2 == null) { return(false); } var result = true; if (string.Compare(NickName.Trim(), a2.NickName.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(FirstName.Trim(), a2.FirstName.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(MiddleInitial.Trim(), a2.MiddleInitial.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(LastName.Trim(), a2.LastName.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(Company.Trim(), a2.Company.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(Line1.Trim(), a2.Line1.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(Line2.Trim(), a2.Line2.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(Line3.Trim(), a2.Line3.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(RegionBvin.Trim(), a2.RegionBvin.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(City.Trim(), a2.City.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(PostalCode.Trim(), a2.PostalCode.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(CountryBvin.Trim(), a2.CountryBvin.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(Phone.Trim(), a2.Phone.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(Fax.Trim(), a2.Fax.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } if (string.Compare(WebSiteUrl.Trim(), a2.WebSiteUrl.Trim(), true, CultureInfo.InvariantCulture) != 0) { result = false; } //if (this.Residential != a2.Residential) { // result = false; //} return(result); }
private void CreateSymmetryMesh(MyVector3 profilenormal_, double radius_) { int n = this.profile.Count; // base stroke point count int m = this.trajectory1.Count; // ref stroke point count // sweep the points, duplicate n times and offsetting List <double> vertices = new List <double>(); foreach (MyVector3 points in profile) { vertices.AddRange(points.ToArray()); } // set base stroke main axis MyVector3 dir = profilenormal_.Normalize(); MyVector3 basecenter3 = new MyVector3(); for (int i = 0; i < n; i++) { basecenter3 += this.profile[i]; } basecenter3 /= n; int closestidx = -1; //3d double dis = double.MaxValue; for (int i = 0; i < n; i++) { double d = (this.profile[i] - this.trajectory1.First()).Length(); if (d < dis) { closestidx = i; dis = d; } } double basescale = radius_; MyVector3 o3 = basecenter3; double tx = 0, ty = 0, tz = 0; double sn = 1; List <MyVector3> onerow = new List <MyVector3>(); onerow.AddRange(this.profile); for (int i = 0; i < m; i++) { Line3 normalline = new Line3(o3, dir); MyVector3 refp1 = normalline.ProjectToLine(this.trajectory1[i]); MyVector3 refp0 = normalline.ProjectToLine(onerow[closestidx]); MyVector3 tv = refp1 - refp0; tx += tv.x; ty += tv.y; tz += tv.z; o3 += tv; double d1 = (this.trajectory1[i] - refp1).Length(); sn = d1 / basescale; double[] new_points = new double[n * 3]; //one row onerow.Clear(); for (int j = 0, k = 0; j < n; ++j, k += 3) //move base point { new_points[k] = vertices[k] + tx; new_points[k + 1] = vertices[k + 1] + ty; new_points[k + 2] = vertices[k + 2] + tz; MyVector3 tp = new MyVector3(new_points, k); double oplength = (o3 - tp).Length(); oplength = oplength * sn; MyVector3 opdir = (tp - o3).Normalize(); double t = oplength; new_points[k] = opdir.x * t + o3.x; new_points[k + 1] = opdir.y * t + o3.y; new_points[k + 2] = opdir.z * t + o3.z; onerow.Add(new MyVector3(new_points, k)); } vertices.AddRange(new_points); } List <int> findices = new List <int>(); for (int i = 0; i < m; ++i) { for (int j = 0; j < n - 1; ++j) { int s = i * n + j, t = i * n + j + 1; int p = (i + 1) * n + j, q = (i + 1) * n + j + 1; // s-t-p, t-p-q findices.Add(s); findices.Add(p); findices.Add(t); findices.Add(t); findices.Add(p); findices.Add(q); } } this.objectmesh = new Mesh(vertices, findices); }
public void Column() { var k3d = new Toolkit(); var logger = new MessageLogger(); double length = 4.0; var p0 = new Point3(0, 0, 0); var p1 = new Point3(0, 0, length); var axis = new Line3(p0, p1); var resourcePath = @""; // get a material from the material table in the folder 'Resources' var materialPath = Path.Combine(resourcePath, "Materials/MaterialProperties.csv"); var inMaterials = k3d.Material.ReadMaterialTable(materialPath); var material = inMaterials.Find(x => x.name == "Steel"); // get a cross section from the cross section table in the folder 'Resources' var crosecPath = Path.Combine(resourcePath, "CrossSections/CrossSectionValues.bin"); CroSecTable inCroSecs = k3d.CroSec.ReadCrossSectionTable(crosecPath, out var info); var crosec_family = inCroSecs.crosecs.FindAll(x => x.family == "FRQ"); var crosec_initial = crosec_family.Find(x => x.name == "FRQ45/5"); // attach the material to the cross section crosec_initial.setMaterial(material); // create the column var beams = k3d.Part.LineToBeam(new List <Line3> { axis }, new List <string>() { "B1" }, new List <CroSec>() { crosec_initial }, logger, out var out_points); // create supports var supports = new List <Support>(); supports.Add(k3d.Support.Support(p0, new List <bool>() { true, true, true, false, false, true })); supports.Add(k3d.Support.Support(p1, new List <bool>() { true, true, false, false, false, false })); // create a Point-load var loads = new List <Load> { k3d.Load.PointLoad(p1, new Vector3(0, 0, -100)) }; // create the model var model = k3d.Model.AssembleModel(beams, supports, loads, out info, out var mass, out var cog, out var message, out var warning); // calculate Th.I response model = k3d.Algorithms.AnalyzeThI(model, out var out_max_disp, out var out_g, out var out_comp, out message); // optimize the cross section model = k3d.Algorithms.OptiCroSec(model, crosec_family, out var maxDisplacements, out var compliances, out message); // disassemble the model k3d.Model.Disassemble(model, logger, out var points, out var lines, out var meshes, out beams, out var shells, out supports, out loads, out var materials, out var crosecs, out var joints); // check the buckling length,; a negative value means that the length was autogenerated Assert.AreEqual(beams[0].BucklingLength(BuilderElementStraightLine.BucklingDir.bklY), -length, 1E-10); // check the resulting cross section Assert.AreEqual(beams[0].BucklingLength(BuilderElementStraightLine.BucklingDir.bklY), -length, 1E-10); Assert.AreEqual(beams[0].crosec.name, "FRQ70/6"); }
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); //initalisieren des Loggers in diesem Reader public static Result ReadPostGIS(bool is3d, double minDist, string Host, int Port, string User, string Password, string DBname, string schema, string tintable, string tincolumn, string tinidcolumn, int tinid, bool postgis_bl, string bl_column, string bl_table, string bl_tinid) { double scale = 1.0; var result = new Result(); // var tinB = Tin.CreateBuilder(true); Dictionary <int, Line3> breaklines = new Dictionary <int, Line3>(); try { //prepare string for database connection string connString = string.Format( "Host={0};Port={1};Username={2};Password={3};Database={4};", Host, Port, User, Password, DBname ); //TIN Request using (var conn = new NpgsqlConnection(connString)) { //open database connection conn.Open(); NpgsqlConnection.GlobalTypeMapper.UseLegacyPostgis(); //ÜBERARBEITEN ggf. weitere Request-Möglichkeiten??? //select request for tin without breaklines via TIN ID string tin_select = "SELECT " + "ST_AsEWKT(" + tincolumn + ") as wkt FROM " + schema + "." + tintable + " WHERE " + tinidcolumn + " = " + tinid; //select request for breaklines via TIN ID + JOIN string bl_select = null; if (postgis_bl == true) { bl_select = "SELECT ST_AsEWKT(" + bl_table + "." + bl_column + ") FROM " + schema + "." + bl_table + " JOIN " + schema + "." + tintable + " ON (" + bl_table + "." + bl_tinid + " = " + tintable + "." + tinidcolumn + ") WHERE " + tintable + "." + tinidcolumn + " = " + tinid; } //TIN abfragen using (var command = new NpgsqlCommand(tin_select, conn)) { var reader = command.ExecuteReader(); Logger.Info("The following REQUEST have been sent: \n" + tin_select); while (reader.Read()) { //read column --> as WKT string geom_string = (reader.GetValue(0)).ToString(); //Split - CRS & TIN string[] geom_split = geom_string.Split(';'); //String for EPSG - Code --> Weiterverarbeitung??? string tin_epsg = geom_split[0]; //Gesamtes TIN string tin_gesamt = geom_split[1]; //Split für den Anfang des TINs char[] trim = { 'T', 'I', 'N', '(' }; tin_gesamt = tin_gesamt.TrimStart(trim); //Split für jedes Dreieck string[] separator = { ")),((" }; string[] tin_string = tin_gesamt.Split(separator, System.StringSplitOptions.RemoveEmptyEntries); //Jedes Dreieck durchlaufen int pnr = 0; //initalisieren foreach (string face in tin_string) { //Punkte - Split über Komma string[] face_points = face.Split(','); //Split über Leerzeichen //FirstCorner string[] P1 = face_points[0].Split(' '); double P1X = Convert.ToDouble(P1[0], CultureInfo.InvariantCulture); double P1Y = Convert.ToDouble(P1[1], CultureInfo.InvariantCulture); double P1Z = Convert.ToDouble(P1[2], CultureInfo.InvariantCulture); //P1 var p1 = Point3.Create(P1X * scale, P1Y * scale, P1Z * scale); //SecoundCorner string[] P2 = face_points[1].Split(' '); double P2X = Convert.ToDouble(P2[0], CultureInfo.InvariantCulture); double P2Y = Convert.ToDouble(P2[1], CultureInfo.InvariantCulture); double P2Z = Convert.ToDouble(P2[2], CultureInfo.InvariantCulture); //P2 var p2 = Point3.Create(P2X * scale, P2Y * scale, P2Z * scale); //ThirdCorner string[] P3 = face_points[2].Split(' '); double P3X = Convert.ToDouble(P3[0], CultureInfo.InvariantCulture); double P3Y = Convert.ToDouble(P3[1], CultureInfo.InvariantCulture); double P3Z = Convert.ToDouble(P3[2], CultureInfo.InvariantCulture); //P3 var p3 = Point3.Create(P3X * scale, P3Y * scale, P3Z * scale); //Punkte hinzufügen & jeweils eine Punktnummer hochzählen tinB.AddPoint(pnr++, p1); tinB.AddPoint(pnr++, p2); tinB.AddPoint(pnr++, p3); //Schleife zum erzeugen des Dreiecks for (int i = pnr - 3; i < pnr; i++) { tinB.AddTriangle(i++, i++, i++); } } } conn.Close(); } //TIN aus TIN-Builder erzeugen Tin tin = tinB.ToTin(out var pointIndex2NumberMap, out var triangleIndex2NumberMap); //Result beschreiben result.Tin = tin; if (postgis_bl == true) { conn.Open(); //Bruchkanten abfragen int index_poly = 0; int index = 0; using (var command = new NpgsqlCommand(bl_select, conn)) { var reader = command.ExecuteReader(); Logger.Info("The following REQUEST have been sent: \n" + bl_select); while (reader.Read()) { string polyline_string = (reader.GetValue(0)).ToString(); string[] poly_split = polyline_string.Split(';'); //Gesamte Polyline string poly_gesamt = poly_split[1]; //Split für den Anfang des TINs char[] trim = { 'L', 'I', 'N', 'E', 'S', 'T', 'R', 'I', 'N', 'G', '(' }; poly_gesamt = poly_gesamt.TrimStart(trim); char[] trimEnd = { ')' }; poly_gesamt = poly_gesamt.TrimEnd(trimEnd); //Split für jeden Punkt string[] separator = { "," }; string[] polyline = poly_gesamt.Split(separator, System.StringSplitOptions.RemoveEmptyEntries); //Jeden Punkt in der Polyline durchlaufen int i = 0; int j = 1; do { string[] point_start_values = polyline[i].Split(' '); double p1X = Convert.ToDouble(point_start_values[0], CultureInfo.InvariantCulture); double p1Y = Convert.ToDouble(point_start_values[1], CultureInfo.InvariantCulture); double p1Z = Convert.ToDouble(point_start_values[2], CultureInfo.InvariantCulture); Point3 p1 = Point3.Create(p1X * scale, p1Y * scale, p1Z * scale); string[] point_end_values = polyline[j].Split(' '); double p2X = Convert.ToDouble(point_end_values[0], CultureInfo.InvariantCulture); double p2Y = Convert.ToDouble(point_end_values[1], CultureInfo.InvariantCulture); double p2Z = Convert.ToDouble(point_end_values[2], CultureInfo.InvariantCulture); Point3 p2 = Point3.Create(p2X * scale, p2Y * scale, p2Z * scale); Vector3 v12 = Vector3.Create(p2); Direction3 d12 = Direction3.Create(v12, scale); Line3 l = Line3.Create(p1, d12); try { breaklines.Add(index, l); //Breakline hinzufügen index++; } catch { index++; } i++; j++; } while (j < polyline.Length); index_poly++; } result.Breaklines = breaklines; } //close database connection conn.Close(); } Logger.Info("All database connections have been disconnected."); Logger.Info("Reading PostGIS successful"); Logger.Info(result.Tin.Points.Count() + " points; " + result.Tin.NumTriangles + " triangels processed"); } } catch (Exception e) { // Console.WriteLine(e.ToString()); Logger.Error("Database connection failed!"); } Console.ReadLine(); return(result); }
private static List <Triangle> Cut(Triangle tri, Line3 line) { // Create coordinate system Vec3 norm = tri.Normal; Vec3 xaxis = tri.Edge13.Normalized; Vec3 yaxis = Vec3.Cross(norm, xaxis); // Convert to 2d space Vec2 v1 = Vec2.Zero; // Consider point 0 the origin Vec2 v2 = Collapse(tri.Item2 - tri.Item1, xaxis, yaxis); // Relative position of Item2 Vec2 v3 = Collapse(tri.Item3 - tri.Item1, xaxis, yaxis); // Relative position of Item3 Vec2 ls = Collapse(line.Item1 - tri.Item1, xaxis, yaxis); Vec2 le = Collapse(line.Item2 - tri.Item1, xaxis, yaxis); Line2 l12 = new Line2(v1, v2); Line2 l13 = new Line2(v1, v3); Line2 l23 = new Line2(v2, v3); Line2 cut = new Line2(ls, le); // Perform intersection test double k12, k13, k23; double c12, c13, c23; bool i12 = l12.Intersects(cut, out c12, out k12); bool i13 = l13.Intersects(cut, out c13, out k13); bool i23 = l23.Intersects(cut, out c23, out k23); // Check cases List <Triangle> tris = new List <Triangle>(); // Corner - Edge intersection (3 cases) if (IsCorner(c12, c13) && IsEdge(c23)) { // Crossing from corner 1 to edge 2->3 Vec3 midpoint = Vec3.Lerp(tri.Item2, tri.Item3, c23); tris.Add(new Triangle(tri.Item1, tri.Item2, midpoint)); tris.Add(new Triangle(tri.Item1, midpoint, tri.Item3)); return(tris); } else if (IsCorner(c13, c23) && IsEdge(c12)) { // Crossing from corner 3 to edge 1->2 Vec3 midpoint = Vec3.Lerp(tri.Item1, tri.Item2, c12); tris.Add(new Triangle(tri.Item1, midpoint, tri.Item3)); tris.Add(new Triangle(midpoint, tri.Item2, tri.Item3)); return(tris); } else if (IsCorner(c12, c23) && IsEdge(c13)) { // Crossing from corner 2 to edge 1->3 Vec3 midpoint = Vec3.Lerp(tri.Item1, tri.Item3, c13); tris.Add(new Triangle(tri.Item1, tri.Item2, midpoint)); tris.Add(new Triangle(midpoint, tri.Item2, tri.Item3)); return(tris); } // Edge - Edge intersection (3 cases) else if (IsEdge(c12) && IsEdge(c23)) { // Collision crossing 1->2, 2->3 Vec3 m1 = Vec3.Lerp(tri.Item1, tri.Item2, c12); Vec3 m2 = Vec3.Lerp(tri.Item2, tri.Item3, c23); Vec3 mid = (tri.Item1 + tri.Item3) / 2; tris.Add(new Triangle(tri.Item1, m1, mid)); tris.Add(new Triangle(m1, m2, mid)); tris.Add(new Triangle(mid, m2, tri.Item3)); tris.Add(new Triangle(m1, tri.Item2, m2)); return(tris); } else if (IsEdge(c12) && IsEdge(c13)) { // Collision crossing 1->3, 1->2 Vec3 m1 = Vec3.Lerp(tri.Item1, tri.Item2, c12); Vec3 m2 = Vec3.Lerp(tri.Item1, tri.Item3, c13); Vec3 mid = (tri.Item2 + tri.Item3) / 2; tris.Add(new Triangle(m1, tri.Item2, mid)); tris.Add(new Triangle(m1, mid, m2)); tris.Add(new Triangle(m2, mid, tri.Item3)); tris.Add(new Triangle(tri.Item1, m1, m2)); return(tris); } else if (IsEdge(c13) && IsEdge(c23)) { // Collision crossing 1->3, 2->3 Vec3 m1 = Vec3.Lerp(tri.Item1, tri.Item3, c13); Vec3 m2 = Vec3.Lerp(tri.Item2, tri.Item3, c23); Vec3 mid = (tri.Item1 + tri.Item2) / 2; tris.Add(new Triangle(mid, tri.Item2, m2)); tris.Add(new Triangle(mid, m2, m1)); tris.Add(new Triangle(tri.Item1, mid, m1)); tris.Add(new Triangle(m1, m2, tri.Item3)); return(tris); } // No intersection (1 case) else { tris.Add(tri); return(tris); } }
private void AreEqual_ClosestPoints(Line3 line, Sphere sphere, Vector3 expectedLine, Vector3 expectedSphere) { Closest.LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out Vector3 linePoint, out Vector3 centerPoint); AreEqual(linePoint, expectedLine); AreEqual(centerPoint, expectedSphere); }
// todo: check public static Vector3 ProjectionPoint(this Line3 l, Vector3 p) { return(l.a + l.ab * ((p - l.a).MultS(l.ab) / l.Len2)); }
private void True_Intersect(Line3 line, Sphere sphere, Vector3 expected) { Assert.True(Intersect.LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out IntersectionLineSphere intersection), format, line, sphere); Assert.AreEqual(IntersectionType.Point, intersection.type, format, line, sphere); AreEqual(intersection.pointA, expected); }
private void False_Intersect(Line3 line, Sphere sphere) { IntersectionLineSphere intersection; Assert.False(Intersect.LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out intersection), format, line, sphere); }
public void TwoPointLoads() { var k3d = new Toolkit(); var logger = new MessageLogger(); double length = 4.0; var p0 = new Point3(0, 0, 0); var p1 = new Point3(length, 0, 0); var axis = new Line3(p0, p1); var resourcePath = @""; // get a material from the material table in the folder 'Resources' var materialPath = Path.Combine(resourcePath, "Materials/MaterialProperties.csv"); var inMaterials = k3d.Material.ReadMaterialTable(materialPath); var material = inMaterials.Find(x => x.name == "Steel"); // get a cross section from the cross section table in the folder 'Resources' var crosecPath = Path.Combine(resourcePath, "CrossSections/CrossSectionValues.bin"); CroSecTable inCroSecs = k3d.CroSec.ReadCrossSectionTable(crosecPath, out var info); var crosec_family = inCroSecs.crosecs.FindAll(x => x.family == "FRQ"); var crosec_initial = crosec_family.Find(x => x.name == "FRQ45/5"); // attach the material to the cross section crosec_initial.setMaterial(material); // create the column var beams = k3d.Part.LineToBeam(new List <Line3> { axis }, new List <string>() { "B1" }, new List <CroSec>() { crosec_initial }, logger, out var out_points); // create supports var supports = new List <Support> { k3d.Support.Support(p0, new List <bool>() { true, true, true, true, true, true }), }; // create a Point-load var loads = new List <Load> { k3d.Load.PointLoad(p1, new Vector3(0, 100, 0), new Vector3(), "LC0"), k3d.Load.PointLoad(p1, new Vector3(0, 0, 50), new Vector3(), "LC1") }; // create the model var model = k3d.Model.AssembleModel(beams, supports, loads, out info, out var mass, out var cog, out var message, out var is_warning); // calculate the displacements ThIAnalyze.solve(model, out var outMaxDisp, out var outG, out var outComp, out var warning, out model); Assert.AreEqual(54.338219302231252, outMaxDisp[0], 1e-5); Assert.AreEqual(27.169109651115626, outMaxDisp[1], 1e-5); }
public Line3 Estimate(List <MyVector3> points) { int iter = 200; if (points.Count == 0) { return(null); } Random rd = new Random(); MyVector3 A = new MyVector3(); MyVector3 B = new MyVector3(); int maxpointinline = int.MinValue; for (int i = 0; i < iter; i++) { A = points[rd.Next(points.Count)]; B = points[rd.Next(points.Count)]; if (A == B) { continue; //if can't generate line } Line3 testline = new Line3(A, (B - A).Normalize()); List <MyVector3> tempinliers = new List <MyVector3>(); int inlierscount = 0; for (int j = 0; j < points.Count; j++) { if (points[j] != A && points[j] != B) { if (testline.DistanceToLine(points[j]) < thres) { tempinliers.Add(points[j]); inlierscount++; } } } if (inlierscount > maxpointinline) { maxpointinline = inlierscount; this.bestline = testline.Copy(); this.inliers.Clear(); foreach (MyVector3 p in tempinliers) { this.inliers.Add(p); } } if (inlierscount >= probability * points.Count) { break; } } if (this.inliers.Count != 0) { double mint = double.MaxValue; double maxt = double.MinValue; foreach (MyVector3 p in this.inliers) { double t = this.bestline.ComputeT(p); if (t > maxt) { maxt = t; } if (t < mint) { mint = t; } } bestline.SetPoints(mint, maxt); } if (bestline != null && bestline.startpoint.x != double.NaN && bestline.startpoint.y != double.NaN && bestline.startpoint.z != double.NaN && bestline.endpoint.x != double.NaN && bestline.endpoint.y != double.NaN && bestline.endpoint.z != double.NaN && (!bestline.startpoint.IsNull() && !bestline.endpoint.IsNull())) { return(bestline); } else { return(null); } }
public string ToHtmlString() { StringBuilder sb = new StringBuilder(); if (NickName.Trim().Length > 0) { sb.Append("<em>" + NickName + "</em><br />"); } if (LastName.Length > 0 || FirstName.Length > 0) { sb.Append(FirstName); if (MiddleInitial.Trim().Length > 0) { sb.Append(" " + MiddleInitial); } sb.Append(" " + LastName + "<br />"); if (Company.Trim().Length > 0) { sb.Append(Company + "<br />"); } } if (Line1.Length > 0) { sb.Append(Line1 + "<br />"); } if (Line2.Trim().Length > 0) { sb.Append(Line2 + "<br />"); } if (Line3.Trim().Length > 0) { sb.Append(Line3 + "<br />"); } MerchantTribe.Web.Geography.Country c = MerchantTribe.Web.Geography.Country.FindByBvin(CountryBvin); MerchantTribe.Web.Geography.Region r = c.Regions.Where(y => y.Abbreviation == RegionBvin).FirstOrDefault(); if (r != null) { sb.Append(City + ", " + r.Abbreviation + " " + _PostalCode + "<br />"); } else { if (RegionName.Trim().Length > 0) { sb.Append(City + ", " + RegionName + " " + _PostalCode + "<br />"); } else { sb.Append(City + ", " + _PostalCode + "<br />"); } } if (c != null) { sb.Append(c.DisplayName + "<br />"); } if (Phone.Trim().Length > 0) { sb.Append(Phone + "<br />"); } if (Fax.Trim().Length > 0) { sb.Append("Fax: " + Fax + "<br />"); } if (WebSiteUrl.Trim().Length > 0) { sb.Append(WebSiteUrl + "<br />"); } return(sb.ToString()); }
private void RayTracein3DPlane(List <MyVector3> points, MyVector3 curp, MyVector3 curdire, MyVector3 sectionPlaneNormal, out int norInsec, out int notNorInsec) { // Param double insecPs_Dist_theshold = 0.01; double insecP_DistBetweenRay_theshold = 20; MyVector3 cutNormal = sectionPlaneNormal.Cross(curdire).Normalize(); ray = new Line3(curp, cutNormal); norInsec = -1; // Normal side notNorInsec = -1; // Not Normal side double dist_left = double.MaxValue; double dist_right = double.MaxValue; for (int i = 0; i < points.Count; i++) { double dist_temp = ray.DistanceToLine(points[i]); if ((points[i] - curp).Dot(cutNormal) > 0) { // Normal side if (dist_left > dist_temp) { dist_left = dist_temp; norInsec = i; } } else { // Not Normal side if (dist_right > dist_temp) { dist_right = dist_temp; notNorInsec = i; } } } if (norInsec == -1) { norInsec = notNorInsec; System.Console.WriteLine("Warining: norInsec == -1"); return; } else if (notNorInsec == -1) { notNorInsec = norInsec; System.Console.WriteLine("Warining: notNorInsec == -1"); return; } else if (norInsec == -1 && notNorInsec == -1) { System.Console.WriteLine("Error: Ray Tracein3DPlane, no intersection points"); return; } if (MyVector3.Distance(points[norInsec], points[notNorInsec]) < insecPs_Dist_theshold) { // this two intersection is too close, so let them become same one.s System.Console.WriteLine("Warining: two intersection is too close"); norInsec = notNorInsec; return; } if (ray.DistanceToLine(points[norInsec]) > insecP_DistBetweenRay_theshold || ray.DistanceToLine(points[notNorInsec]) > insecP_DistBetweenRay_theshold) { System.Console.WriteLine("Warining: two intersection is too far"); // this two intersection is too far, so let them become same one.s norInsec = notNorInsec; return; } }
public StraightTrajectory(Line3 trajectory, bool isSection = true) { Trajectory = trajectory; IsSection = isSection; }
private void AreEqual_ClosestPoint(Line3 line, Vector3 point, Vector3 expected) { AreEqual(Closest.PointLine(point, line), expected); }
public void Beam() { var k3d = new Toolkit(); var logger = new MessageLogger(); double length = 4.0; var p0 = new Point3(0, 0, 0); var p1 = new Point3(length, 0, 0); var axis = new Line3(p0, p1); var resourcePath = Path.Combine(Utils.PluginPathExe(), @"..\..\Resources\"); // get a material from the material table in the folder 'Resources' var materialPath = Path.Combine(resourcePath, "MaterialProperties.csv"); var inMaterials = k3d.Material.ReadMaterialTable(materialPath); var material = inMaterials.Find(x => x.name == "Steel"); // get a cross section from the cross section table in the folder 'Resources' var crosecPath = Path.Combine(resourcePath, "CrossSectionValues.csv"); CroSecTable inCroSecs = k3d.CroSec.ReadCrossSectionTable(crosecPath, out var info); var crosec_family = inCroSecs.crosecs.FindAll(x => x.family == "FRQ"); var crosec_initial = crosec_family.Find(x => x.name == "FRQ45/5"); // attach the material to the cross section crosec_initial.setMaterial(material); // create the column var beams = k3d.Part.LineToBeam(new List <Line3> { axis }, new List <string>() { "B1" }, new List <CroSec>() { crosec_initial }, logger, out var out_points); // create supports var supports = new List <Support> { k3d.Support.Support(p0, new List <bool>() { true, true, true, true, false, false }), k3d.Support.Support(p1, new List <bool>() { false, true, true, false, false, false }) }; // create a Point-load var loads = new List <Load> { k3d.Load.PointLoad(p1, new Vector3(0, 0, -100)) }; // create the model var model = k3d.Model.AssembleModel(beams, supports, loads, out info, out var mass, out var cog, out var message, out var warning); // calculate the natural vibrations int from_shape_ind = 1; int shapes_num = 1; int max_iter = 100; double eps = 1e-8; var disp_dummy = new List <double>(); var scaling = EigenShapesScalingType.matrix; model = k3d.Algorithms.NaturalVibes(model, from_shape_ind, shapes_num, max_iter, eps, disp_dummy, scaling, out List <double> nat_frequencies, out List <double> modal_masses, out List <Vector3> participation_facs, out List <double> participation_facs_disp, out model); Assert.AreEqual(nat_frequencies[0], 8.9828263788644716, 1e-8); }
private void AreEqual_Distance(Line3 line, Vector3 point, float expected = 0) { AreEqual(Distance.PointLine(point, line), expected); }
private static bool Equal(Line3 first, Line3 second) => first.a == second.a && first.b == second.b;