public static void create(long key, WayReference endPoint, string materialId) { bool isNode1 = endPoint.isNode1(NodeIndex.getPosById(key)); bool isSmall = endPoint.SmallWay; Vector3 originalScale = endPoint.gameObject.transform.localScale; Vector3 nodeCameraPos = Game.getCameraPosition(isNode1 ? endPoint.node1 : endPoint.node2); Vector3 fromPos = nodeCameraPos - new Vector3(0f, originalScale.y / 2f, 0f); Vector3 toPos = fromPos + new Vector3((isSmall ? originalScale.x / 2f : originalScale.y / 2f), originalScale.y, 0f); GameObject endPointObj = MapSurface.createPlaneMeshForPoints(fromPos, toPos, MapSurface.Anchor.LEFT_CENTER); endPointObj.name = "End of way (" + key + ")"; Vector3 zOffset = new Vector3(0, 0, Game.WAYS_Z_POSITION); endPointObj.transform.position = endPointObj.transform.position + zOffset - (isNode1 ? Vector3.zero : endPoint.transform.rotation * new Vector3(isSmall ? originalScale.x / 2f : originalScale.y / 2f, 0f, 0f)); endPointObj.transform.parent = Game.instance.waysParent; endPointObj.transform.rotation = endPoint.transform.rotation; AutomaticMaterialObject endPointMaterialObject = endPointObj.AddComponent <AutomaticMaterialObject> () as AutomaticMaterialObject; endPointMaterialObject.requestMaterial(materialId, null); // TODO - Default material // Add rigidbody and mesh collider, so that they will fall onto the underlying plane Misc.AddGravityToWay(endPointObj); Misc.AddWayObjectComponent(endPointObj); }
public void RotatePoint() { var map = new MapSurface(); map.SetRotation(Math.PI / 2); // Rotate about center point 500, 500 -> 0, 0 map.Rotate_point(1000.0, 0.0, out var toX, out var toY); toX.Should().BeApproximately(0.0, 0.0000001); toY.Should().BeApproximately(0.0, 0.0000001); }
private void ResetMap() { MapLevel.ResetArrows(); var levelInfo = levelGrid[playerPos.x, playerPos.y]; // Set new positions Map.transform.position = levelInfo.Pos; Player.FruitEntity.transform.position = levelInfo.Pos; MapSurface.ClearSurface(); }
// Use this for initialization void Start() { // Grab props from MapSurface MapSurface mapSurface = GetComponent <MapSurface>(); center = mapSurface.calculatedCenter; rotation = mapSurface.calculatedRotation; width = mapSurface.calculatedWidth; height = mapSurface.calculatedHeight; // TODO - Doesn't really work right now - continue this later with a fresh mind... createLines(); }
public void ZeroRotationDoesNotMovepoint(double testX, double testY) { var map = new MapSurface(); map.SetRotation(0); // Rotate/unrotate about center point 500, 500 map.Rotate_point(testX, testY, out var toX, out var toY); toX.Should().BeApproximately(testX, 0.0000001); toY.Should().BeApproximately(testY, 0.0000001); }
private void createLines() { GameObject linesParent = new GameObject("Lines"); linesParent.transform.parent = transform; linesParent.transform.localPosition = center + new Vector3(0f, 0f, -0.01f); // Side lines Vector3 sideLineOuter = new Vector3(LINE_THICKNESS, 0f, 0f); Vector3 sideLineInner = new Vector3(width - LINE_THICKNESS, LINE_THICKNESS, 0f); GameObject leftLine = MapSurface.createPlaneMeshForPoints(sideLineOuter, sideLineInner); GameObject rightLine = MapSurface.createPlaneMeshForPoints(sideLineOuter, sideLineInner); // Use WayLine for setting white material WayLine.SetWhiteMaterial(leftLine); WayLine.SetWhiteMaterial(rightLine); // Locate left line correctly leftLine.name = "Left outer"; leftLine.transform.parent = linesParent.transform; leftLine.transform.localPosition = (Quaternion.Euler(0f, 0f, rotation) * new Vector3(-LINE_THICKNESS, -height / 2f + LINE_THICKNESS * 1.5f, 0f)); leftLine.transform.rotation = Quaternion.Euler(0f, 0f, rotation); // Locate right line correctly rightLine.name = "Right outer"; rightLine.transform.parent = linesParent.transform; rightLine.transform.localPosition = (Quaternion.Euler(0f, 0f, rotation) * new Vector3(LINE_THICKNESS, height / 2f - LINE_THICKNESS * 1.5f, 0f)); rightLine.transform.rotation = Quaternion.Euler(0f, 0f, rotation); // Goal lines Vector3 goalLineOuter = new Vector3(0f, LINE_THICKNESS, 0f); Vector3 goalLineInner = new Vector3(LINE_THICKNESS, height - LINE_THICKNESS, 0f); GameObject goalLineClose = MapSurface.createPlaneMeshForPoints(goalLineOuter, goalLineInner); GameObject goalLineFar = MapSurface.createPlaneMeshForPoints(goalLineOuter, goalLineInner); // Use WayLine for setting white material WayLine.SetWhiteMaterial(goalLineClose); WayLine.SetWhiteMaterial(goalLineFar); // Locate close goal line correctly goalLineClose.name = "Close goal line"; goalLineClose.transform.parent = linesParent.transform; goalLineClose.transform.localPosition = (Quaternion.Euler(0f, 0f, rotation) * new Vector3(-width / 2f + LINE_THICKNESS * 1.5f, -LINE_THICKNESS, 0f)); goalLineClose.transform.rotation = Quaternion.Euler(0f, 0f, rotation); // Locate far goal line correctly goalLineFar.name = "Far goal line"; goalLineFar.transform.parent = linesParent.transform; goalLineFar.transform.localPosition = (Quaternion.Euler(0f, 0f, rotation) * new Vector3(width / 2f - LINE_THICKNESS * 1.5f, LINE_THICKNESS, 0f)); goalLineFar.transform.rotation = Quaternion.Euler(0f, 0f, rotation); }
private GameObject getLineForWay(Vector3 way, float lineHeight = -1f) { Vector3 fromPos = -way / 2; Vector3 toPos = way / 2; if (lineHeight == -1f) { lineHeight = GetLineHeight(); } toPos = new Vector3(toPos.x, fromPos.y + lineHeight, toPos.z); return(MapSurface.createPlaneMeshForPoints(fromPos, toPos)); }
public void RotatePointRoundtrip(double testX, double testY) { var map = new MapSurface(); map.SetRotation(Math.PI / 2); // Rotate/unrotate about center point 500, 500 -> 1000, 0 map.Rotate_point(testX, testY, out var toX, out var toY); map.Un_rotate_point(toX, toY, out var toX2, out var toY2); toX2.Should().BeApproximately(testX, 0.0000001); toY2.Should().BeApproximately(testY, 0.0000001); }
/* TODO ColorPaletteClassType() * private TICDisplayPaletteBaseClass ColorPaletteClassType() * { * case FMode of * ...Height : Result = TICDisplayPalette_Height; * ...CCV : Result = TICDisplayPalette_CCV; * ...CCVPercent : Result = TICDisplayPalette_CCVPercent; * ...Latency : Result = TICDisplayPalette_RadioLatency; * ...PassCount : Result = TICDisplayPalette_PassCount; * ...PassCountSummary : Result = TICDisplayPalette_PassCountSummary; // Palettes are fixed three color palettes - display will use direct transitions * ...RMV : Result = TICDisplayPalette_RMV; * ...Frequency : Result = TICDisplayPalette_Frequency; * ...Amplitude : Result = TICDisplayPalette_Amplitude; * ...CutFill : Result = TICDisplayPalette_CutFill; * ...Moisture : Result = TICDisplayPalette_Moisture; * ...TemperatureSummary : Result = TICDisplayPaletteBase; //TICDisplayPalette_Temperature; * ...GPSMode : Result = TICDisplayPaletteBase; //TICDisplayPalette_GPSMode; * ...CCVSummary : Result = TICDisplayPaletteBase; //TICDisplayPalette_CCVSummary; * ...CCVPercentSummary : Result = TICDisplayPalette_CCVPercent; * ...CompactionCoverage : Result = TICDisplayPalette_CoverageOverlay; * ...VolumeCoverage : Result = TICDisplayPalette_VolumeOverlay; * ...MDP : Result = TICDisplayPalette_MDP; // ajr15167 * ...MDPSummary : Result = TICDisplayPaletteBase; * ...MDPPercent : Result = TICDisplayPalette_MDPPercent; * ...MDPPercentSummary : Result = TICDisplayPalette_MDPPercent; * ...MachineSpeed : Result = TICDisplayPalette_MachineSpeed; * ...CCVPercentChange : Result = TICDisplayPalette_CCVPercent; * ...TargetThicknessSummary : Result = TICDisplayPalette_VolumeOverlay; * ...TargetSpeedSummary : Result = TICDisplayPalette_SpeedSummary; * ...CCVChange : Result = TICDisplayPalette_CCVChange; * ...CCA : Result = TICDisplayPalette_CCA; * ...CCASummary : Result = TICDisplayPalette_CCASummary; * * else * SIGLogMessage.PublishNoODS(Self, Format('ColorPaletteClassType: Unknown display type: %d', [Ord(FMode)]), ...Assert); * Result = TICDisplayPaletteBase; * end; * end; */ /* TODO: ComputeCCAPalette * function ComputeCCAPalette :Boolean; * var * I, J, K :Integer; * ResponseVerb :...VerbBase; * ServerResult :TICServerRequestResult; * ResponseDataStream :TStream; * CCAMinimumPasses :...CCAMinPassesValue; * CCAColorScale :...CCAColorScale; * CCAPalette :TColorPalettes; * * begin * Result = False; * * ResponseVerb = nil; * try * if Length(FFilter1.Machines) > 0 then * FMachineID = FFilter1.Machines[0].ID // Must be set by caller * else * FMachineID = -1; // will fail call * if not Assigned(ASNodeImplInstance) or ASNodeImplInstance.ServiceStopped then * begin * SIGLogMessage.PublishNoODS(Self, Format('%s.Execute: Aborting request as service has been stopped', [Self.ClassName]), ...Warning); * Exit; * end; * * ASNodeImplInstance.PSLoadBalancer.LoadBalancedPSService.GetMachineCCAMinimumPassesValue(FDataModelID, FMachineID, FFilter1.StartTime, FFilter1.EndTime, FFilter1.LayerID, ResponseVerb); * if Assigned(ResponseVerb) then * with ResponseVerb as ...Verb_SendResponse do * begin * ServerResult = TICServerRequestResult(ResponseCode); * ResponseDataStream = ResponseData; * if (ServerResult = ...NoError) and assigned(ResponseData) then * begin * CCAMinimumPasses = ReadSmallIntFromStream(ResponseDataStream); * * Result = CCAMinimumPasses > 0; * * if not Result then * Exit; * * CCAColorScale = ...CCAColorScaleManager.CreateCoverageScale(CCAMinimumPasses); * try * SetLength(CCAPalette.Transitions, CCAColorScale.TotalColors); * * J = Low(CCAPalette.Transitions); * k = High(CCAPalette.Transitions); * for I = J to K do * begin * CCAPalette.Transitions[I].Color = CCAColorScale.ColorSegments[K - I].Color; * CCAPalette.Transitions[I].Value = I+1; * end; * CCAPalette.ConvertRGBToBGR; // gets done again but needed to match Anatoli palette test :) * WorkingColorPalette.PopulateFromPaletteColors(CCAPalette); * WorkingColorPalette.TransitionColors.ValuesCount = Length(CCAPalette.Transitions); * finally * if Assigned(CCAColorScale) then * FreeAndNil(CCAColorScale); * end; * end * else * SIGLogMessage.PublishNoODS(Self, Format('%s.Execute: GetMachineCCAMinimumPassesValue Failed for InternalSiteModelMachineIndex: %d. ReturnCode:%d', [Self.ClassName, FMachineID, Ord(ServerResult)]), ...Warning); * end; * finally * if Assigned(ResponseVerb) then * FreeAndNil(ResponseVerb); * end; * end; */ /* TODO: CreateAndInitialiseWorkingColorPalette * function CreateAndInitialiseWorkingColorPalette :Boolean; * begin * Result = True; * * // Create a scaled palette to use when rendering the data * try * if ColorPaletteClassType<> Nil then * begin * * WorkingColorPalette = ColorPaletteClassType.Create; * WorkingColorPalette.SmoothPalette = FMode = ...CutFill; * * // CCASummary is done per machine id * if FMode in [...CCA, ...CCASummary] * then * Result = ComputeCCAPalette * else * begin * if Length(FColorPalettes.Transitions) = 0 then * WorkingColorPalette.SetToDefaults * else * WorkingColorPalette.PopulateFromPaletteColors(FColorPalettes); * end; * * if Result then * WorkingColorPalette.ComputePalette; * end * else * WorkingColorPalette = Nil; * * Except * On e:Exception do * SIGLogMessage.PublishNoODS(Self, Format('%s.Execute: Error: %s ', [Self.ClassName, e.Message]), ...Exception); * end; * end; */ /// <summary> /// Renders all sub grids in a representational style that indicates where there is data, but nothing else. This is used for large scale displays /// (zoomed out a lot) where meaningful detail cannot be drawn on the tile /// </summary> private SKBitmap RenderTileAsRepresentationalDueToScale(ISubGridTreeBitMask overallExistenceMap) { using (var RepresentationalDisplay = PVMDisplayerFactory.GetDisplayer(Mode /*, FICOptions*/)) { using (var mapView = new MapSurface { SquareAspect = false }) { mapView.SetRotation(TileRotation); RepresentationalDisplay.MapView = mapView; RepresentationalDisplay.MapView.SetBounds(NPixelsX, NPixelsY); RepresentationalDisplay.MapView.SetWorldBounds(NEECoords[0].X, NEECoords[0].Y, NEECoords[0].X + WorldTileWidth, NEECoords[0].Y + WorldTileHeight, 0); // Iterate over all the bits in the sub grids drawing a rectangle for each one on the tile being rendered if (overallExistenceMap.ScanSubGrids(RotatedTileBoundingExtents, leaf => { leaf.CalculateWorldOrigin(out var WorldOriginX, out var WorldOriginY); (leaf as SubGridTreeLeafBitmapSubGrid)?.Bits.ForEachSetBit((x, y) => { RepresentationalDisplay.MapView.DrawRect(WorldOriginX + (x * overallExistenceMap.CellSize), WorldOriginY + (y * overallExistenceMap.CellSize), overallExistenceMap.CellSize, overallExistenceMap.CellSize, true, RepresentColor); }); return(true); })) { // Remove the canvas from the map view to prevent it's disposal (it's being returned to the caller) var canvas = RepresentationalDisplay.MapView.BitmapCanvas; RepresentationalDisplay.MapView.BitmapCanvas = null; return(canvas); } } } return(null); // It did not work out... }
public void createBuildingWithXMLNode(XmlNode xmlNode) { if (xmlNode != null) { // this.gameObject.name = "BuildingRoof (" + xmlNode.Attributes.GetNamedItem ("id").Value + ")"; this.id = xmlNode.Attributes.GetNamedItem("id").Value; this.gameObject.name = "Building (" + id + ")"; Rigidbody buildingRigidBody = this.gameObject.AddComponent <Rigidbody>(); Misc.SetGravityState(this.gameObject, false); // Create the roof and set its layer roof = new GameObject("BuildingRoof (" + xmlNode.Attributes.GetNamedItem("id").Value + ")"); roof.transform.parent = this.transform; roof.transform.localPosition = Vector3.zero; MapSurface roofSurface = roof.AddComponent <MapSurface>(); roof.AddComponent <BuildingRoofLayer>(); roofSurface.createMesh(xmlNode); roofSurface.createMeshCollider(false); } }
public void Creation() { var map = new MapSurface(); map.Should().NotBeNull(); }
private static void addMeshToGameObject(GameObject gameObject, Vector2[] vertices2D) { // Use the triangulator to get indices for creating triangles Triangulator tr = new Triangulator(vertices2D); int[] indices = tr.Triangulate(); // Create the Vector3 vertices Vector3[] vertices = new Vector3[vertices2D.Length]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new Vector3(vertices2D[i].x, vertices2D[i].y, 0); } // Create the mesh Mesh msh = new Mesh(); msh.vertices = vertices; msh.triangles = indices; msh.uv = vertices2D; msh.RecalculateNormals(); msh.RecalculateBounds(); // Calculate mesh area float area = Misc.getMeshArea(msh); gameObject.AddComponent <MeshArea>().area = area; // Add center point to the surface MapSurface mapSurface = gameObject.GetComponent <MapSurface>(); if (mapSurface == null) { mapSurface = gameObject.AddComponent <MapSurface>(); } mapSurface.calculatedCenter = Misc.GetCenterOfVectorList(vertices2D); mapSurface.rect = Misc.GetRectOfVectorList(vertices2D); // Try to guess a rotation and width/height - will only really be applicable for the simplest forms (rectangular'ish) // Assume that the rotation is where the longest single line is Vector2 longestVector = Misc.GetLongestDistanceVector(vertices2D); float zRotation = Quaternion.FromToRotation(Vector3.right, longestVector).eulerAngles.z; // Debug.Log("Z1: " + Quaternion.FromToRotation(Vector3.right, longestVector).eulerAngles.z + ", Z2: " + Misc.ToDegrees(Mathf.Atan(longestVector.y / longestVector.x))); mapSurface.calculatedRotation = zRotation; // Debug.Log("Longest vector:"); // DebugFn.print(longestVector); // Debug.Log("Deg: " + mapSurface.calculatedRotation); Vector2 longestVectorApprox90Deg = Misc.GetLongestDistanceVector90DegXFrom(vertices2D, mapSurface.calculatedRotation, 15f); // Debug.Log("Diff°:" + Quaternion.FromToRotation(longestVector, longestVectorApprox90Deg).eulerAngles.z); mapSurface.calculatedWidth = longestVector.magnitude; mapSurface.calculatedHeight = longestVectorApprox90Deg.magnitude; // Vector3[] normals = msh.normals; // for (int i = 0; i < normals.Length; i++) { // normals[i] = normal; // } // msh.SetNormals (normals.ToList ()); //// msh.RecalculateNormals(); //// msh.RecalculateBounds(); // Set up game object with mesh; gameObject.AddComponent(typeof(MeshRenderer)); MeshFilter filter = gameObject.AddComponent(typeof(MeshFilter)) as MeshFilter; filter.mesh = msh; }
// Update is called once per frame public void Update() { switch (GameState) { case State.Init: break; case State.Play: if (Player == null || Countdown <= 0) { mapTimer.Stop(); GameOver(false); } foreach (var bot in spawnedBots) { if (bot == null) { continue; } var other = bot.GetComponentInChildren <FruitEntity>(); if (other == null || other.Squishes == null || Player == null || Player.FruitEntity == null || Player.FruitEntity.Squishes == null) { continue; } if (Player.FruitEntity.Squishes.Eatable(other)) { other.DisplaySquishIndicator(false); } else if (other.Squishes.Eatable(Player.FruitEntity)) { other.DisplaySquishIndicator(true); } else { other.HideSquishIndicator(); } } PlayerCoverage = MapSurface.PaintCoverage(Player.FruitEntity.Color); // Player has enough coverage if (PlayerCoverage >= CoverageWinPerc) { mapTimer.Stop(); KillAllBots(); // Complete Level var levelInfo = levelGrid[playerPos.x, playerPos.y]; levelGrid[playerPos.x, playerPos.y].Completed = true; levelsCompleted++; Game.Find().UIManager.SetLevelCompleted(levelInfo.Id); if (levelsCompleted == GridSize * GridSize) { GameOver(true); } Player.FruitEntity.transform.position = levelInfo.Pos; StartTravelState(); } break; case State.Travel: PlayerCoverage = MapSurface.PaintCoverage(Player.FruitEntity.Color); break; } }
private static void CreateCurvedDashedLines(GameObject parent, long key, List <WayReference> wayReferences) { GameObject curveDashedLines = new GameObject(); curveDashedLines.name = "Curved dashed lines"; curveDashedLines.transform.SetParent(parent.transform); curveDashedLines.transform.localPosition = Vector3.zero; Pos centerPos = NodeIndex.getPosById(key); Vector3 posPosition = Game.getCameraPosition(centerPos); WayReference firstReference = wayReferences [0]; WayReference secondReference = wayReferences [1]; bool firstIsNode1 = firstReference.isNode1(centerPos); bool secondIsNode1 = secondReference.isNode1(centerPos); GameObject wayFirst = firstReference.gameObject; GameObject waySecond = secondReference.gameObject; // Number of fields in opposite direction float fieldsFromPos2 = firstReference.getNumberOfFieldsInDirection(false); // Number of fields in own direction float fieldsFromPos1 = firstReference.getNumberOfFieldsInDirection(true); // Number of fields in total float numberOfFields = firstReference.getNumberOfFields(); List <float> dashedLineFields = new List <float> (); for (float field = 1; field < fieldsFromPos2; field++) { dashedLineFields.Add(field); } for (float field = 1; field < fieldsFromPos1; field++) { dashedLineFields.Add(fieldsFromPos2 + field); } // If ways have same "isNode1", invert y-axis for second way bool isSameNode1 = firstIsNode1 == secondIsNode1; // Way width float wayHeight = wayFirst.transform.localScale.y; float wayHeightCompensated = wayHeight - GetLineHeight() * 2f; foreach (float field in dashedLineFields) { GameObject curveDashes = new GameObject(); curveDashes.name = "Curved dashes"; curveDashes.transform.SetParent(curveDashedLines.transform); curveDashes.transform.localPosition = Vector3.zero; // Percentual position of way width, where to put middle line float percentualPositionY = field / numberOfFields; // Get our points (center in y-axis) float yPositionInWay = percentualPositionY * wayHeightCompensated; float dashYMiddle = -wayHeightCompensated / 2f + GetLineHeight() + yPositionInWay - GetDashedLineHeight() / 2f; float dashYMiddleSameNode1 = wayHeightCompensated / 2f - yPositionInWay + GetDashedLineHeight() / 2f; Vector3 firstPosMiddle = posPosition + wayFirst.transform.rotation * new Vector3((firstIsNode1 ? 1 : -1) * wayHeight / 2f, dashYMiddle, 0); Vector3 secondPosMiddle = posPosition + waySecond.transform.rotation * new Vector3((secondIsNode1 ? 1 : -1) * wayHeight / 2f, isSameNode1 ? dashYMiddleSameNode1 : dashYMiddle, 0); Vector3 halfDashedLineHeight = new Vector3(0, GetDashedLineHeight() / 2f, 0); // Get our points (top in y-axis) Vector3 wayFirstHalfDashedHeight = wayFirst.transform.rotation * halfDashedLineHeight; Vector3 waySecondHalfDashedHeight = waySecond.transform.rotation * halfDashedLineHeight; Vector3 firstPosTop = firstPosMiddle - wayFirstHalfDashedHeight; Vector3 secondPosTop = secondPosMiddle + (isSameNode1 ? 1 : -1) * waySecondHalfDashedHeight; // Get our points (bottom in y-axis) Vector3 firstPosBottom = firstPosMiddle + wayFirstHalfDashedHeight; Vector3 secondPosBottom = secondPosMiddle + (isSameNode1 ? -1 : 1) * waySecondHalfDashedHeight; Quaternion firstRotation = firstIsNode1 ? WayHelper.ONEEIGHTY_DEGREES * wayFirst.transform.rotation : wayFirst.transform.rotation; Quaternion secondRotation = secondIsNode1 ? WayHelper.ONEEIGHTY_DEGREES * waySecond.transform.rotation : waySecond.transform.rotation; Vector3 firstDirection = firstRotation * Vector3.right; Vector3 secondDirection = secondRotation * Vector3.right; Vector3 intersectionPoint; Vector3 intersectionPointTop; Vector3 intersectionPointBottom; bool intersectionFound = Math3d.LineLineIntersection(out intersectionPoint, firstPosMiddle, firstDirection, secondPosMiddle, secondDirection); if (!intersectionFound && firstRotation.eulerAngles.z == secondRotation.eulerAngles.z) { intersectionFound = true; intersectionPoint = firstPosMiddle + ((secondPosMiddle - firstPosMiddle) / 2); intersectionPointTop = firstPosTop + ((secondPosTop - firstPosTop) / 2); intersectionPointBottom = firstPosBottom + ((secondPosBottom - firstPosBottom) / 2); } else { Math3d.LineLineIntersection(out intersectionPointTop, firstPosTop, firstDirection, secondPosTop, secondDirection); Math3d.LineLineIntersection(out intersectionPointBottom, firstPosBottom, firstDirection, secondPosBottom, secondDirection); } // TODO - Shouldn't be needed - debug only if (!intersectionFound) { Debug.Log("ERR: " + key); return; } // 1. Get bezier length for curve float bezierLength = Math3d.GetBezierLength(firstPosMiddle, intersectionPoint, secondPosMiddle); // 2. Decide how many dashes to fit, with gaps (also calculate each dash and gap length) // If only one line float numberOfLines = 1f; float dashedLineWidth = bezierLength; float dashedLineGap = 0f; // If more lines if (bezierLength > DASHED_LINE_WIDTH + CITY_DASHED_LINE_GAP) { float totalWidth = 0f; for (numberOfLines = 2f; ; numberOfLines++) { totalWidth = DASHED_LINE_WIDTH + (DASHED_LINE_WIDTH + CITY_DASHED_LINE_GAP) * (numberOfLines - 1); if (totalWidth >= bezierLength) { break; } } dashedLineWidth = DASHED_LINE_WIDTH * bezierLength / totalWidth; dashedLineGap = CITY_DASHED_LINE_GAP * bezierLength / totalWidth; } // 3. Calculate each dash along the line t (time) on bezier curve List <KeyValuePair <float, float> > dashTimes = new List <KeyValuePair <float, float> > (); if (numberOfLines == 1f) { dashTimes.Add(new KeyValuePair <float, float>(0f, 1f)); } else { dashTimes.Add(new KeyValuePair <float, float>(0f, dashedLineWidth / bezierLength)); for (float lineStart = dashedLineWidth + dashedLineGap; lineStart < bezierLength; lineStart += dashedLineWidth + dashedLineGap) { float lineStartTime = lineStart / bezierLength; dashTimes.Add(new KeyValuePair <float, float>(lineStartTime, lineStartTime + dashedLineWidth / bezierLength)); } } foreach (KeyValuePair <float, float> dashTime in dashTimes) { float startTime = dashTime.Key; float endTime = dashTime.Value; float dashLengthPercent = endTime - startTime; float numberOfPoints = Mathf.Max(bezierLength / dashLengthPercent * WayHelper.BEZIER_RESOLUTION, 4f); float eachPointTime = dashLengthPercent / numberOfPoints; List <Vector3> dashPoints = new List <Vector3>(); // Top line for (float t = startTime; t <= endTime; t += eachPointTime) { dashPoints.Add(Math3d.GetVectorInBezierAtTime(t, firstPosTop, intersectionPointTop, secondPosTop)); } // Bottom line for (float t = endTime; t >= startTime; t -= eachPointTime) { dashPoints.Add(Math3d.GetVectorInBezierAtTime(t, firstPosBottom, intersectionPointBottom, secondPosBottom)); } GameObject lineMiddle = MapSurface.createPlaneMeshForPoints(dashPoints); lineMiddle.name = "Curved dash"; WayLine.SetWhiteMaterial(lineMiddle); lineMiddle.transform.SetParent(curveDashes.transform); lineMiddle.transform.localPosition = new Vector3(0, 0, -0.01f); } } }
private static void CreateCurvedMiddleLine(GameObject parent, long key, List <WayReference> wayReferences) { Pos centerPos = NodeIndex.getPosById(key); Vector3 posPosition = Game.getCameraPosition(centerPos); WayReference firstReference = wayReferences [0]; WayReference secondReference = wayReferences [1]; bool firstIsNode1 = firstReference.isNode1(centerPos); bool secondIsNode1 = secondReference.isNode1(centerPos); GameObject wayFirst = firstReference.gameObject; GameObject waySecond = secondReference.gameObject; // Number of fields in opposite direction float fieldsFromPos2 = firstReference.getNumberOfFieldsInDirection(false); // Number of fields in total float numberOfFields = firstReference.getNumberOfFields(); // Percentual position of way width, where to put middle line float percentualPositionY = fieldsFromPos2 / numberOfFields; // Way width float wayHeight = wayFirst.transform.localScale.y - GetLineHeight() * 2f; // TODO - What if the wayReference is REALLY short Vector3 wayFirstSize = wayFirst.transform.localScale; Quaternion wayFirstRotation = wayFirst.transform.rotation; float wayFirstWayWidth = (firstIsNode1 ? 1 : -1) * wayFirstSize.y; float lineFirstPositionAdjustment = percentualPositionY * wayHeight; Vector3 wayFirstMiddleLineTopPos = posPosition + wayFirstRotation * new Vector3(wayFirstWayWidth / 2f, -wayHeight / 2f + lineFirstPositionAdjustment, 0f); Vector3 wayFirstMiddleLineBottomPos = posPosition + wayFirstRotation * new Vector3(wayFirstWayWidth / 2f, -wayHeight / 2f + GetLineHeight() + lineFirstPositionAdjustment, 0f); Vector3 waySecondSize = waySecond.transform.localScale; Quaternion waySecondRotation = waySecond.transform.rotation; float waySecondWayWidth = (secondIsNode1 ? 1 : -1) * waySecondSize.y; float lineSecondPositionAdjustment = percentualPositionY * wayHeight; Vector3 waySecondMiddleLineTopPos = posPosition + waySecondRotation * new Vector3(waySecondWayWidth / 2f, -wayHeight / 2f + lineSecondPositionAdjustment, 0f); Vector3 waySecondMiddleLineBottomPos = posPosition + waySecondRotation * new Vector3(waySecondWayWidth / 2f, -wayHeight / 2f + GetLineHeight() + lineSecondPositionAdjustment, 0f); if (IsTopAndBottomCrossing(wayFirstMiddleLineTopPos, waySecondMiddleLineTopPos, wayFirstMiddleLineBottomPos, waySecondMiddleLineBottomPos)) { Vector3 tmp = wayFirstMiddleLineBottomPos; wayFirstMiddleLineBottomPos = wayFirstMiddleLineTopPos; wayFirstMiddleLineTopPos = tmp; } // DebugFn.square (wayFirstMiddleLineTopPos); // DebugFn.square (wayFirstMiddleLineBottomPos); // DebugFn.square (waySecondMiddleLineTopPos); // DebugFn.square (waySecondMiddleLineBottomPos); List <Vector3> linePoints = new List <Vector3> (); linePoints.Add(wayFirstMiddleLineTopPos); AddBezierPoints(linePoints, wayFirstMiddleLineTopPos, wayFirstRotation, waySecondMiddleLineTopPos, waySecondRotation); linePoints.Add(waySecondMiddleLineBottomPos); AddBezierPoints(linePoints, waySecondMiddleLineBottomPos, waySecondRotation, wayFirstMiddleLineBottomPos, wayFirstRotation); GameObject lineMiddle = MapSurface.createPlaneMeshForPoints(linePoints); lineMiddle.name = "Curved middle line"; WayLine.SetWhiteMaterial(lineMiddle); lineMiddle.transform.SetParent(parent.transform); lineMiddle.transform.localPosition = new Vector3(0, 0, -0.01f); }
/// <summary> /// Executor that implements requesting and rendering sub grid information to create the rendered tile /// </summary> public async Task <SKBitmap> ExecuteAsync() { // WorkingColorPalette : TICDisplayPaletteBase; _log.LogInformation($"Performing Execute for DataModel:{DataModelID}, Mode={Mode}"); ApplicationServiceRequestStatistics.Instance.NumMapTileRequests.Increment(); /* * if Assigned(ASNodeImplInstance.RequestCancellations) and * ASNodeImplInstance.RequestCancellations.IsRequestCancelled(FExternalDescriptor) then * begin * if ...SvcLocations.Debug_LogDebugRequestCancellationToFile then * SIGLogMessage.PublishNoODS(Self, 'Request cancelled: ' + FExternalDescriptor.ToString, ...Debug); * * ResultStatus = ...RequestHasBeenCancelled; * InterlockedIncrement64(ASNodeRequestStats.NumMapTileRequestsCancelled); * Exit; * end; * * // The governor is intended to restrict the numbers of heavy weight processes * // such as pipelines that interact with the PC layer to request sub grids * ScheduledWithGovernor = ASNodeImplInstance.Governor.Schedule(FExternalDescriptor, Self, gqWMS, ResultStatus); * if not ScheduledWithGovernor then * Exit; */ var RequestDescriptor = Guid.NewGuid(); if (_log.IsDebugEnabled()) { if (CoordsAreGrid) { _log.LogDebug($"RenderPlanViewTiles Execute: Performing render for request={RequestDescriptor} Args: Project={DataModelID}, Mode={Mode}, CutFillDesign=''{CutFillDesign}'' " + $"Bound[BL/TR:X/Y]=({BLPoint.X} {BLPoint.Y}, {TRPoint.X} {TRPoint.Y}), Width={NPixelsX}, Height={NPixelsY}"); } else { _log.LogDebug($"RenderPlanViewTiles Execute: Performing render for request={RequestDescriptor} Args: Project={DataModelID}, Mode={Mode}, CutFillDesign=''{CutFillDesign}'' " + $"Bound[BL/TR:Lon/Lat]=({BLPoint.X} {BLPoint.Y}, {TRPoint.X} {TRPoint.Y}), Width={NPixelsX}, Height={NPixelsY}"); } // Include the details of the filters with the logged tile parameters if (Filters != null) { for (var i = 0; i < Filters.Filters.Length; i++) { _log.LogDebug($"Filter({i}): {Filters.Filters[i]}"); } } } // Determine the grid (NEE) coordinates of the bottom/left, top/right WGS-84 positions // given the project's coordinate system. If there is no coordinate system then exit. var SiteModel = DIContext.Obtain <ISiteModels>().GetSiteModel(DataModelID); if (SiteModel == null) { _log.LogWarning($"Failed to locate site model {DataModelID}"); return(null); } _log.LogInformation($"Got Site model {DataModelID}, production data extents are {SiteModel.SiteModelExtent}"); LLHCoords = new[] { new XYZ(BLPoint.X, BLPoint.Y, 0), new XYZ(TRPoint.X, TRPoint.Y, 0), new XYZ(BLPoint.X, TRPoint.Y, 0), new XYZ(TRPoint.X, BLPoint.Y, 0) }; _log.LogInformation($"LLHCoords for tile request {string.Concat(LLHCoords)}, CoordsAreGrid {CoordsAreGrid}"); if (CoordsAreGrid) { NEECoords = LLHCoords; } else { NEECoords = DIContext .Obtain <ICoreXWrapper>() .LLHToNEE(SiteModel.CSIB(), LLHCoords.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians) .ToTRex_XYZ(); } _log.LogInformation($"After conversion NEECoords are {string.Concat(NEECoords)}"); WorldTileHeight = MathUtilities.Hypot(NEECoords[0].X - NEECoords[2].X, NEECoords[0].Y - NEECoords[2].Y); WorldTileWidth = MathUtilities.Hypot(NEECoords[0].X - NEECoords[3].X, NEECoords[0].Y - NEECoords[3].Y); var dx = NEECoords[2].X - NEECoords[0].X; var dy = NEECoords[2].Y - NEECoords[0].Y; // Calculate the tile rotation as the mathematical angle turned from 0 (due east) to the vector defined by dy/dx TileRotation = Math.Atan2(dy, dx); // Convert TileRotation to represent the angular deviation rather than a bearing TileRotation = (Math.PI / 2) - TileRotation; RotatedTileBoundingExtents.SetInverted(); NEECoords.ForEach(xyz => RotatedTileBoundingExtents.Include(xyz.X, xyz.Y)); _log.LogInformation($"Tile render executing across tile: [Rotation:{TileRotation}, {MathUtilities.RadiansToDegrees(TileRotation)} degrees] " + $" [BL:{NEECoords[0].X}, {NEECoords[0].Y}, TL:{NEECoords[2].X},{NEECoords[2].Y}, " + $"TR:{NEECoords[1].X}, {NEECoords[1].Y}, BR:{NEECoords[3].X}, {NEECoords[3].Y}] " + $"World Width, Height: {WorldTileWidth}, {WorldTileHeight}, Rotated bounding extents: {RotatedTileBoundingExtents}"); // Construct the renderer, configure it, and set it on its way // WorkingColorPalette = Nil; using (var Renderer = new PlanViewTileRenderer()) { try { // Intersect the site model extents with the extents requested by the caller var adjustedSiteModelExtents = SiteModel.GetAdjustedDataModelSpatialExtents(null); _log.LogInformation($"Calculating intersection of bounding box and site model {DataModelID}:{adjustedSiteModelExtents}"); var dataSelectionExtent = new BoundingWorldExtent3D(RotatedTileBoundingExtents); dataSelectionExtent.Intersect(adjustedSiteModelExtents); if (!dataSelectionExtent.IsValidPlanExtent) { ResultStatus = RequestErrorStatus.InvalidCoordinateRange; _log.LogInformation($"Site model extents {adjustedSiteModelExtents}, do not intersect RotatedTileBoundingExtents {RotatedTileBoundingExtents}"); using var mapView = new MapSurface(); mapView.SetBounds(NPixelsX, NPixelsY); var canvas = mapView.BitmapCanvas; mapView.BitmapCanvas = null; return(canvas); } // Compute the override cell boundary to be used when processing cells in the sub grids // selected as a part of this pipeline // Increase cell boundary by one cell to allow for cells on the boundary that cross the boundary SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MinX, dataSelectionExtent.MinY, SiteModel.CellSize, SubGridTreeConsts.DefaultIndexOriginOffset, out var CellExtents_MinX, out var CellExtents_MinY); SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MaxX, dataSelectionExtent.MaxY, SiteModel.CellSize, SubGridTreeConsts.DefaultIndexOriginOffset, out var CellExtents_MaxX, out var CellExtents_MaxY); var CellExtents = new BoundingIntegerExtent2D(CellExtents_MinX, CellExtents_MinY, CellExtents_MaxX, CellExtents_MaxY); CellExtents.Expand(1); var filterSet = FilterUtilities.ConstructFilters(Filters, VolumeType); // Construct PipelineProcessor using var processor = DIContext.Obtain <IPipelineProcessorFactory>().NewInstanceNoBuild <SubGridsRequestArgument>( RequestDescriptor, DataModelID, GridDataFromModeConverter.Convert(Mode), new SubGridsPipelinedResponseBase(), filterSet, CutFillDesign, DIContext.Obtain <Func <PipelineProcessorTaskStyle, ITRexTask> >()(PipelineProcessorTaskStyle.PVMRendering), DIContext.Obtain <Func <PipelineProcessorPipelineStyle, ISubGridPipelineBase> >()(PipelineProcessorPipelineStyle.DefaultProgressive), DIContext.Obtain <IRequestAnalyser>(), Utilities.DisplayModeRequireSurveyedSurfaceInformation(Mode) && Utilities.FilterRequireSurveyedSurfaceInformation(Filters), requestRequiresAccessToDesignFileExistenceMap: Utilities.RequestRequiresAccessToDesignFileExistenceMap(Mode, CutFillDesign), CellExtents, LiftParams ); if (filterSet.Filters.Length == 3) { var pipeline = processor.Pipeline as SubGridPipelineProgressive <SubGridsRequestArgument, SubGridRequestsResponse>; pipeline.SubGridsRequestComputeStyle = SubGridsRequestComputeStyle.SimpleVolumeThreeWayCoalescing; } // Set the PVM rendering rexTask parameters for progressive processing processor.Task.TRexNodeID = RequestingTRexNodeID; ((IPVMRenderingTask)processor.Task).TileRenderer = Renderer; // Set the spatial extents of the tile boundary rotated into the north reference frame of the cell coordinate system to act as // a final restriction of the spatial extent used to govern data requests processor.OverrideSpatialExtents.Assign(RotatedTileBoundingExtents); // Prepare the processor if (!processor.Build()) { _log.LogError($"Failed to build pipeline processor for request to model {SiteModel.ID}"); ResultStatus = RequestErrorStatus.FailedToConfigureInternalPipeline; return(null); } // Test to see if the tile can be satisfied with a representational render indicating where // data is but not what it is (this is useful when the zoom level is far enough away that we // cannot meaningfully render the data). If the size of s sub grid is smaller than // the size of a pixel in the requested tile then do this. Just check the X dimension // as the data display is isotropic. // TODO: Could this be done before creation of the pipeline processor? if (Utilities.SubGridShouldBeRenderedAsRepresentationalDueToScale(WorldTileWidth, WorldTileHeight, NPixelsX, NPixelsY, processor.OverallExistenceMap.CellSize)) { return(RenderTileAsRepresentationalDueToScale(processor.OverallExistenceMap)); // There is no need to do anything else } /* TODO - Create a scaled palette to use when rendering the data * // Create a scaled palette to use when rendering the data * if not CreateAndInitialiseWorkingColorPalette then * begin * SIGLogMessage.PublishNoODS(Self, Format('Failed to create and initialise working color palette for data: %s in datamodel %d', [TypInfo.GetEnumName(TypeInfo(TICDisplayMode), Ord(FMode)), FDataModelID]), ...Warning); * Exit; * end; */ // Renderer.WorkingPalette = WorkingColorPalette; Renderer.IsWhollyInTermsOfGridProjection = true; // Ensure the renderer knows we are using grid projection coordinates Renderer.SetBounds(RotatedTileBoundingExtents.CenterX - WorldTileWidth / 2, RotatedTileBoundingExtents.CenterY - WorldTileHeight / 2, WorldTileWidth, WorldTileHeight, NPixelsX, NPixelsY); Renderer.TileRotation = TileRotation; var performRenderStopWatch = Stopwatch.StartNew(); ResultStatus = Renderer.PerformRender(Mode, processor, ColorPalettes, Filters, LiftParams); _log.LogInformation($"Renderer.PerformRender completed in {performRenderStopWatch.Elapsed}"); if (processor.Response.ResultStatus == RequestErrorStatus.OK) { var canvas = Renderer.Displayer.MapView.BitmapCanvas; Renderer.Displayer.MapView.BitmapCanvas = null; return(canvas); } } catch (Exception e) { _log.LogError(e, "Exception occurred"); ResultStatus = RequestErrorStatus.Exception; } } return(null); }
public static void create(long key, List <WayReference> wayReferences, string materialId) { if (off) { return; } Pos pos = NodeIndex.getPosById(key); // Sort based on rotation in the point closest to the intersection wayReferences.Sort(delegate(WayReference x, WayReference y) { float angleDiff = AngleAroundNode(pos, x) - AngleAroundNode(pos, y); // Ignoring float to int issues (if there are any) int angleDiffInt = (int)angleDiff; return(angleDiffInt); }); // Gather our way bounds (used for checking if ways intersects each other) List <Bounds> wayBounds = getWayBounds(wayReferences); // List of positions where to draw the mesh List <Vector3> meshPoints; List <WayReference> intersectionList = Misc.CloneBaseNodeList(wayReferences); bool isComplex = false; if (intersectionList.Count == 2 && wayBounds [0].Intersects(wayBounds [1])) { // Only two ways and they intersect, special logic meshPoints = getMeshPointsForComplexTwoWay(intersectionList, wayBounds, pos); isComplex = true; } else { meshPoints = getMeshPointsForNonComplex(intersectionList, wayBounds, pos); } // Debug.Log ("Intersection"); GameObject intersectionObj = MapSurface.createPlaneMeshForPoints(meshPoints); intersectionObj.name = "Intersection " + (isComplex ? "complex " : "") + (intersectionList.Count - 1) + "-way (" + key + ")"; Vector3 zOffset = new Vector3(0, 0, Game.WAYS_Z_POSITION); intersectionObj.transform.position = intersectionObj.transform.position + zOffset; intersectionObj.transform.parent = Game.instance.waysParent; AutomaticMaterialObject intersectionMaterialObject = intersectionObj.AddComponent <AutomaticMaterialObject> () as AutomaticMaterialObject; intersectionMaterialObject.requestMaterial(materialId, null); // TODO - Should have same material as connecting way(s) Misc.AddGravityToWay(intersectionObj); Misc.AddWayObjectComponent(intersectionObj); // Need waylines for all straight ways if (wayReferences.Count == 2) { bool wayQualifiedForCrossing = wayReferences[0].way.WayWidthFactor >= WayHelper.LIMIT_WAYWIDTH && wayReferences[0].way.CarWay; if (pos.getTagValue("highway") == "crossing" && wayQualifiedForCrossing) { WayCrossing.Create(intersectionObj, key, wayReferences); } else { WayLine.CreateCurved(intersectionObj, key, wayReferences); } } }
public IEnumerator getCountryData() { // Get country metadata WWW www = CacheWWW.Get(Game.endpointBaseUrl + Game.countryMetaDataRelativeUrl); yield return(www); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(www.text); XmlNodeList countries = xmlDoc.SelectNodes("/countries/country"); int numLeft = countries.Count; foreach (XmlNode country in countries) { // TODO - Remove debug log - and to break after a few Debug.Log(numLeft--); // if (numLeft < 227) { // break; // } XmlAttributeCollection countryAttributes = country.Attributes; string code = Misc.xmlString(countryAttributes.GetNamedItem("code")); string name = Misc.xmlString(countryAttributes.GetNamedItem("name")); // TODO // string code = "ZA"; // string name = "South Africa"; // Country parent GameObject countryParent = new GameObject(name); Country countryObj = countryParent.AddComponent <Country> (); countryObj.countryName = name; countryObj.code = code; countryObj.tag = "Country"; countryParent.transform.parent = transform; // Landarea parent GameObject landareaParent = new GameObject("Land"); landareaParent.transform.parent = countryParent.transform; // Get country full outline data WWW countryWWW = CacheWWW.Get(Game.endpointBaseUrl + Game.countryMetaDataRelativeUrl + Game.countryCodeDataQuerystringPrefix + code); yield return(countryWWW); XmlDocument countryDataDoc = new XmlDocument(); countryDataDoc.LoadXml(countryWWW.text); XmlNodeList polygons = countryDataDoc.SelectNodes("/country/polygons/polygon"); int outerIndex = 0; int innerIndex = 0; foreach (XmlNode polygon in polygons) { // Outer coordinates List <Vector3> outerCoordinates = new List <Vector3>(); yield return(getCoordinates(polygon, "outer", outerCoordinates)); if (outerCoordinates.Count > 0) { GameObject outerPart = MapSurface.createPlaneMeshForPoints(outerCoordinates); outerPart.name = "Outer" + outerIndex++; outerPart.transform.parent = landareaParent.transform; countryObj.addCoords(outerCoordinates); } // Inner coordinates List <Vector3> innerCoordinates = new List <Vector3>(); yield return(getCoordinates(polygon, "inner", innerCoordinates)); if (innerCoordinates.Count > 0) { GameObject innerPart = MapSurface.createPlaneMeshForPoints(innerCoordinates); innerPart.name = "Inner" + innerIndex++; innerPart.transform.parent = landareaParent.transform; innerPart.transform.localPosition = new Vector3(innerPart.transform.localPosition.x, innerPart.transform.localPosition.y, innerPart.transform.localPosition.z - 0.1f); countryObj.addCoords(innerCoordinates, false); } } // Country name GameObject countryNameContainerInstance = Instantiate(countryNameContainer, countryObj.transform) as GameObject; TextMesh countryNameTextMesh = countryNameContainerInstance.GetComponentInChildren <TextMesh> (); countryNameTextMesh.text = name; countryObj.setupDone(); countryObj.saveMeshes(); // break; // TODO } analyzeAndMergeInners(); Debug.Log("Done!"); }