public void RenderMarkers(DisposableIterator apsis_iterator, MapObject.ObjectType type, NodeSource source, Vessel vessel, CelestialBody celestial) { // We render at most 64 markers of one type and one provenance (e.g., at // most 64 perilunes for the prediction of the active vessel). This is // more than is readable, and keeps the size of the map node pool under // control. for (int i = 0; i < 64 && !apsis_iterator.IteratorAtEnd(); ++i, apsis_iterator.IteratorIncrement()) { QP apsis = apsis_iterator.IteratorGetDiscreteTrajectoryQP(); MapNodeProperties node_properties = new MapNodeProperties { visible = true, object_type = type, vessel = vessel, celestial = celestial, world_position = (Vector3d)apsis.q, velocity = (Vector3d)apsis.p, source = source, time = apsis_iterator.IteratorGetDiscreteTrajectoryTime() }; if (pool_index_ == nodes_.Count) { nodes_.Add(MakePoolNode()); } else if (properties_[nodes_[pool_index_]].object_type != type || properties_[nodes_[pool_index_]].source != source) { // KSP attaches labels to its map nodes, but never detaches them. // If the node changes type, we end up with an arbitrary combination of // labels Ap, Pe, AN, DN. // If the node changes source, the colour of the icon label is not // updated to match the icon (making it unreadable in some cases). // Recreating the node entirely takes a long time (approximately // 𝑁 * 70 μs, where 𝑁 is the total number of map nodes in existence), // instead we manually get rid of the labels. foreach (var component in nodes_[pool_index_].transform.GetComponentsInChildren < TMPro.TextMeshProUGUI>()) { if (component.name == "iconLabel(Clone)") { UnityEngine.Object.Destroy(component.gameObject); } } // Ensure that KSP thinks the type changed, and reattaches icon // labels next time around, otherwise we might end up with no labels. // Null nodes do not have a label, so inducing a type change through // Null does not result in spurious labels. properties_[nodes_[pool_index_]].object_type = MapObject.ObjectType.Null; nodes_[pool_index_].NodeUpdate(); } properties_[nodes_[pool_index_++]] = node_properties; } }
public void RenderMarkers(DisposableIterator apsis_iterator, MapObject.ObjectType type, NodeSource source, Vessel vessel, CelestialBody celestial) { for (; !apsis_iterator.IteratorAtEnd(); apsis_iterator.IteratorIncrement()) { QP apsis = apsis_iterator.IteratorGetDiscreteTrajectoryQP(); MapNodeProperties node_properties; node_properties.object_type = type; node_properties.vessel = vessel; node_properties.celestial = celestial; node_properties.world_position = (Vector3d)apsis.q; node_properties.velocity = (Vector3d)apsis.p; node_properties.source = source; node_properties.time = apsis_iterator.IteratorGetDiscreteTrajectoryTime(); if (pool_index_ == nodes_.Count) { nodes_.Add(MakePoolNode()); } else if (properties_[nodes_[pool_index_]].object_type != type) { // Do not reuse a node for different types, as this results in // overlapping labels on KSP 1.3, e.g. a closest approach marker that // also says "Ap" and "DN". nodes_[pool_index_].Terminate(); properties_.Remove(nodes_[pool_index_]); nodes_[pool_index_] = MakePoolNode(); } properties_[nodes_[pool_index_++]] = node_properties; } }
public void RenderMarkers(DisposableIterator apsis_iterator, MapObject.ObjectType type, NodeSource source, ReferenceFrameSelector reference_frame) { MapObject associated_map_object; UnityEngine.Color colour; switch (type) { case MapObject.ObjectType.Apoapsis: case MapObject.ObjectType.Periapsis: CelestialBody fixed_body = reference_frame.selected_celestial; associated_map_object = fixed_body.MapObject; colour = fixed_body.orbit == null ? XKCDColors.SunshineYellow : fixed_body.orbitDriver.Renderer.nodeColor; break; case MapObject.ObjectType.ApproachIntersect: associated_map_object = reference_frame.target_override.mapObject; colour = XKCDColors.Chartreuse; break; case MapObject.ObjectType.AscendingNode: case MapObject.ObjectType.DescendingNode: if (!reference_frame.target_override && (reference_frame.frame_type == ReferenceFrameSelector.FrameType.BODY_CENTRED_NON_ROTATING || reference_frame.frame_type == ReferenceFrameSelector.FrameType.BODY_SURFACE)) { // In one-body frames, the apsides are shown with the colour of the // body. // The nodes are with respect to the equator, rather than with respect // to an orbit. We show the nodes in a different (but arbitrary) // colour so that they can be distinguished easily. associated_map_object = reference_frame.selected_celestial.MapObject; colour = XKCDColors.Chartreuse; } else { // In two-body frames, if apsides are shown, they are shown with the // colour of the secondary (or in XKCD chartreuse if the secondary is // a vessel). // The nodes are with respect to the orbit of the secondary around the // primary. We show the nodes with the colour of the primary. CelestialBody primary = reference_frame.target_override ? reference_frame.selected_celestial : reference_frame.selected_celestial.referenceBody; associated_map_object = primary.MapObject; colour = primary.orbit == null ? XKCDColors.SunshineYellow : primary.orbitDriver.Renderer.nodeColor; } break; default: throw Log.Fatal($"Unexpected type {type}"); } colour.a = 1; for (int i = 0; i < MaxRenderedNodes && !apsis_iterator.IteratorAtEnd(); ++i, apsis_iterator.IteratorIncrement()) { QP apsis = apsis_iterator.IteratorGetDiscreteTrajectoryQP(); MapNodeProperties node_properties = new MapNodeProperties { visible = true, object_type = type, colour = colour, reference_frame = reference_frame, world_position = (Vector3d)apsis.q, velocity = (Vector3d)apsis.p, source = source, time = apsis_iterator.IteratorGetDiscreteTrajectoryTime(), associated_map_object = associated_map_object, }; if (type == MapObject.ObjectType.Periapsis && reference_frame.selected_celestial.GetAltitude( node_properties.world_position) < 0) { node_properties.object_type = MapObject.ObjectType.PatchTransition; node_properties.colour = XKCDColors.Orange; } if (pool_index_ == nodes_.Count) { nodes_.Add(MakePoolNode()); } else if (properties_[nodes_[pool_index_]].object_type != node_properties.object_type || properties_[nodes_[pool_index_]].colour != node_properties.colour) { // KSP attaches labels to its map nodes, but never detaches them. // If the node changes type, we end up with an arbitrary combination of // labels Ap, Pe, AN, DN. // Similarly, if the node changes colour, the colour of the icon label // is not updated to match the icon (making it unreadable in some // cases). Recreating the node entirely takes a long time // (approximately 𝑁 * 70 μs, where 𝑁 is the total number of map nodes // in existence), instead we manually get rid of the labels. foreach (var component in nodes_[pool_index_].transform.GetComponentsInChildren < TMPro.TextMeshProUGUI>()) { if (component.name == "iconLabel(Clone)") { UnityEngine.Object.Destroy(component.gameObject); } } // Ensure that KSP thinks the type changed, and reattaches icon // labels next time around, otherwise we might end up with no labels. // Null nodes do not have a label, so inducing a type change through // Null does not result in spurious labels. Note that the type is // updated only if the node is visible. properties_[nodes_[pool_index_]].visible = true; properties_[nodes_[pool_index_]].object_type = MapObject.ObjectType.Null; nodes_[pool_index_].NodeUpdate(); } properties_[nodes_[pool_index_++]] = node_properties; } }
private static extern void SetVesselStateOffset( IntPtr plugin, [MarshalAs(UnmanagedType.LPStr)] String vessel_guid, QP from_parent);
private static extern void InsertCelestial( IntPtr plugin, int celestial_index, double gravitational_parameter, int parent_index, QP from_parent);
public override TJCRDIR Dir(string file) { QuickStream BT = null; try { BT = QuickStream.ReadFile(file); var ret = new TJCRDIR(); string s; do { if (BT.EOF) { throw new Exception("JQL heading not found!"); } s = RL(BT); } while (s == "" || qstr.Prefixed(s, "#")); if (s != "JQL") { throw new Exception("JQL not properly headed!"); } var optional = true; var author = ""; var notes = ""; while (!BT.EOF) { s = RL(BT); var c = new QP(s); if (s != "" && (!qstr.Prefixed(s, "#"))) { switch (c.commando) { case "REQUIRED": case "REQ": optional = false; break; case "OPTIONAL": case "OPT": optional = true; break; case "PATCH": { var to = c.parameter.IndexOf('>'); if (to < 0) { var p = JCR6.Dir(c.parameter); if (p == null) { if (optional) { break; } throw new Exception($"Patch error {JCR6.JERROR}"); } ret.Patch(p); } else { var rw = c.parameter.Substring(0, to).Trim().Replace("\\", "/"); var tg = c.parameter.Substring(to + 1).Trim().Replace("\\", "/"); var p = JCR6.Dir(rw); if (p == null) { if (optional) { break; } throw new Exception($"Patch error {JCR6.JERROR}"); } ret.Patch(p, tg); } break; } case "AUTHOR": case "AUT": author = c.parameter; break; case "NOTES": case "NTS": notes = c.parameter; break; case "RAW": { var p = c.parameter.IndexOf('>'); var rw = c.parameter.Replace("\\", "/"); var tg = rw; if (p >= 0) { rw = c.parameter.Substring(0, p).Trim().Replace("\\", "/"); tg = c.parameter.Substring(p + 1).Trim().Replace("\\", "/"); } if (tg.Length > 1 && tg[1] == ':') { tg = tg.Substring(2); } while (tg[1] == '/') { tg = tg.Substring(1); } if (rw == "") { throw new Exception("RAW no original"); } if (tg == "") { throw new Exception("RAW no target"); } if (!File.Exists(rw)) { if (optional) { break; } throw new Exception($"Required raw file \"{rw}\" doesn't exist!"); } var e = new TJCREntry(); e.Entry = tg; e.MainFile = rw; e.Storage = "Store"; e.Offset = 0; e.Size = (int)new FileInfo(rw).Length; e.CompressedSize = e.Size; e.Notes = notes; e.Author = author; ret.Entries[tg.ToUpper()] = e; break; } case "TEXT": case "TXT": { var tg = c.parameter.Trim().Replace("\\", "/"); if (tg.Length > 1 && tg[1] == ':') { tg = tg.Substring(2); } while (tg[1] == '/') { tg = tg.Substring(1); } if (tg == "") { throw new Exception("TEXT no target"); } var e = new TJCREntry(); var buf = new byte[5]; e.Entry = tg; e.MainFile = file; e.Storage = "Store"; e.Offset = (int)BT.Position; e.Notes = notes; e.Author = author; do { if (BT.EOF) { throw new Exception("Unexpected end of file (TXT Block not ended)"); } for (int i = 0; i < 4; i++) { buf[i] = buf[i + 1]; } buf[4] = BT.ReadByte(); //Console.WriteLine(Encoding.UTF8.GetString(buf, 0, buf.Length)); } while (Encoding.UTF8.GetString(buf, 0, buf.Length) != "@END@"); RL(BT); e.Size = (int)(BT.Position - 7) - e.Offset; e.CompressedSize = e.Size; ret.Entries[tg.ToUpper()] = e; break; } case "COMMENT": case "CMT": { if (c.parameter == "") { throw new Exception("Comment without a name"); } var cmt = new StringBuilder(""); var l = ""; do { if (BT.EOF) { throw new Exception("Unexpected end of file (COMMENT block not ended)"); } l = RL(BT, false); if (l.Trim() != "@END@") { cmt.Append($"{l}\n"); } } while (l.Trim() != "@END@"); ret.Comments[c.parameter] = cmt.ToString(); break; } case "IMPORT": ret.PatchFile(c.parameter); break; case "END": return(ret); default: throw new Exception($"Unknown instruction! {c.commando}"); } } } return(ret); } catch (Exception e) { JCR6.JERROR = $"JQL error: {e.Message}"; #if DEBUG Console.WriteLine(e.StackTrace); #endif return(null); } finally { if (BT != null) { BT.Close(); } } }
public void RenderMarkers(DisposableIterator apsis_iterator, Provenance provenance, ReferenceFrameSelector reference_frame) { if (!nodes_.ContainsKey(provenance)) { nodes_.Add(provenance, new SingleProvenancePool()); } var pool = nodes_[provenance]; MapObject associated_map_object; UnityEngine.Color colour; switch (provenance.type) { case MapObject.ObjectType.Apoapsis: case MapObject.ObjectType.Periapsis: CelestialBody fixed_body = reference_frame.Centre(); associated_map_object = fixed_body.MapObject; colour = fixed_body.orbit == null ? XKCDColors.SunshineYellow : fixed_body.orbitDriver.Renderer.nodeColor; break; case MapObject.ObjectType.ApproachIntersect: associated_map_object = reference_frame.target.mapObject; colour = XKCDColors.Chartreuse; break; case MapObject.ObjectType.AscendingNode: case MapObject.ObjectType.DescendingNode: if (reference_frame.Centre() == null) { // In two-body frames, if apsides are shown, they are shown with the // colour of the secondary (or in XKCD chartreuse if the secondary is // a vessel). // The nodes are with respect to the orbit of the secondary around the // primary. We show the nodes with the colour of the primary. CelestialBody primary = reference_frame.OrientingBody(); associated_map_object = primary.MapObject; colour = primary.orbit == null ? XKCDColors.SunshineYellow : primary.orbitDriver.Renderer.nodeColor; } else { // In one-body frames, the apsides are shown with the colour of the // body. // The nodes are with respect to the equator, rather than with respect // to an orbit. We show the nodes in a different (but arbitrary) // colour so that they can be distinguished easily. associated_map_object = reference_frame.Centre().MapObject; colour = XKCDColors.Chartreuse; } break; default: throw Log.Fatal($"Unexpected type {provenance.type}"); } colour.a = 1; for (int i = 0; i < MaxNodesPerProvenance && !apsis_iterator.IteratorAtEnd(); ++i, apsis_iterator.IteratorIncrement()) { QP apsis = apsis_iterator.IteratorGetDiscreteTrajectoryQP(); MapNodeProperties node_properties = new MapNodeProperties { visible = true, object_type = provenance.type, colour = colour, reference_frame = reference_frame, world_position = (Vector3d)apsis.q, velocity = (Vector3d)apsis.p, source = provenance.source, time = apsis_iterator.IteratorGetDiscreteTrajectoryTime(), associated_map_object = associated_map_object, }; if (provenance.type == MapObject.ObjectType.Periapsis && reference_frame.Centre().GetAltitude( node_properties.world_position) < 0) { node_properties.object_type = MapObject.ObjectType.PatchTransition; node_properties.colour = XKCDColors.Orange; } if (pool.nodes_used == pool.nodes.Count) { pool.nodes.Add(MakePoolNode()); } properties_[pool.nodes[pool.nodes_used++]] = node_properties; } }