{"id":1190,"date":"2020-01-14T12:16:28","date_gmt":"2020-01-14T12:16:28","guid":{"rendered":"http:\/\/nbodyphysics.com\/blog\/?page_id=1190"},"modified":"2020-01-14T12:16:29","modified_gmt":"2020-01-14T12:16:29","slug":"in-orbit-orbit-point","status":"publish","type":"page","link":"https:\/\/nbodyphysics.com\/blog\/gravity-engine-doc-1-3-2-2-2\/in-orbit-orbit-point\/","title":{"rendered":"In Orbit: Orbit Point"},"content":{"rendered":"\n<p>The OrbitPoint component places the attached game object or NBody at a specific location on an existing orbit. Uses include showing a label for apoapsis and periapsis or indicating a position of a future course change. <\/p>\n\n\n\n<p>This video demonstrates the uses of this component:<\/p>\n\n\n\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"OrbitPoint Tutorial (GE 4.0)\" width=\"625\" height=\"352\" src=\"https:\/\/www.youtube.com\/embed\/VwRkOwjHuF0?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>The choices for the location of an OrbitPoint are:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>APOAPSIS: Farthest point of the orbit (in the case of an ellipse). Not defined for an orbit with eccentricty &gt;= 1.0<\/li><li>PERIAPSIS: Closest point of approach<\/li><li>ALTITUDE: Position at which the radius will be the value indicated in the  Point Data field. If the altitude is outside apo\/peri for the orbit the point will be placed at the apo\/peri point. <\/li><li>ASCENDING_NODE: the point where an inclined orbit cross the XY plane in the ascending direction<\/li><li>DESCENDING_NODE: the point where an inclined orbit crosses the XY plane in a descending direction <\/li><li>FIXED_TIME: a position a fixed GE time ahead of an NBody object specified by the TimeRefBody field. The time offset is specified with Point Data<\/li><li>PHASE: a fixed phase position in the orbit specified by Point Data<\/li><li>PHASE_FROM_MOUSE: An interactive mode that allows a user to click on the orbit to define the location of the orbit point. <\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Inspector Fields<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/nbodyphysics.com\/blog\/wp-content\/uploads\/2020\/01\/OrbitPoint.png\" alt=\"\" class=\"wp-image-1192\" width=\"467\" height=\"250\" srcset=\"https:\/\/nbodyphysics.com\/blog\/wp-content\/uploads\/2020\/01\/OrbitPoint.png 316w, https:\/\/nbodyphysics.com\/blog\/wp-content\/uploads\/2020\/01\/OrbitPoint-300x160.png 300w\" sizes=\"auto, (max-width: 467px) 100vw, 467px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li>Mouse Control: Handle mouse input directly. When disabled a controller can call HandleMouseInput() to allow this component to operate. Only relevant for PHASE_FROM_MOUSE point type. <\/li><li>Orbit Predictor: The orbit on which the orbit point will be placed<\/li><li>Point Type: The type of point (see above)<\/li><li>Point Data: auxillary data. Specific use depends on the point type<\/li><li>Time Wrt Ref Body: The NBody on the orbit specified to be used as a reference point when the point type is FIXED_TIME.<\/li><li>Scene Camera: the camera active in the scene. Only used in PHASE_FROM_MOUSE mode to map 3D location to screen space.  <\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Implementation Notes<\/h2>\n\n\n\n<p>The point type logic is found in an obvious switch() statement in the OrbitPoint Update() method. Several of the point types are straight forward, but the implementation of FIXED_TIME and PHASE_FROM_MOUSE provide useful examples of code that may be useful in other contexts. They are expanded upon below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">FIXED_TIME<\/h3>\n\n\n\n<p>In FIXED_TIME mode the position of the orbit point is determined by finding a point on the orbit that is a specific time ahead of the TimeRefBody. To accomplish this the logic flow is:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Create an OrbitUniversal from the current data of the TimeRefBody and set this to Kepler mode.<\/li><li>Lock this OrbitUniversal object at the required time (current time + the time offset from PointData)<\/li><li>Retrieve the position from the OrbitUniversal and use this to position the OrbitPoint<\/li><li>Destroy the OrbitUniversal<\/li><\/ul>\n\n\n\n<p>In code this is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Don't assume the NBody has an orbit, build a new OrbitData from internal details \n\/\/ Greedy implementation - could move some to SetTime\n\/\/ TODO: Check if things have changed before doing all this every frame\nOrbitUniversal targetOrbit = timeRefBody.gameObject.AddComponent&lt;OrbitUniversal>();\n                targetOrbit.InitFromActiveNBody(timeRefBody, orbitU.centerNbody, OrbitUniversal.EvolveMode.KEPLERS_EQN);\ndouble[] r_new = new double[] { 0, 0, 0 };\ntargetOrbit.LockAtTime(GravityEngine.Instance().GetPhysicalTimeDouble() + pointData);\nVector3d pos = new Vector3d();\nVector3d vel = new Vector3d();\ndouble time0 = 0;\ntargetOrbit.GetRVTLastEvolve(ref pos, ref vel, ref time0);\nif (nbody != null) {\n    ge.SetPositionDoubleV3(nbody, pos);\n    ge.SetVelocityDoubleV3(nbody, vel);\n} else {\n    transform.position = ge.MapPhyPosToWorld(new Vector3((float)r_new[0], (float)r_new[1], (float)r_new[2]));\n}\nMonoBehaviour.Destroy(targetOrbit);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">PHASE_FROM_MOUSE Implementation<\/h3>\n\n\n\n<p>This implementation demonstrates a technique for using player mouse clicks to make reference to objects in the 3D GE world. The mouse is used to indicate a direction with respect to the center body of the orbit. This phase angle is then used to position the OrbitPoint. <\/p>\n\n\n\n<p>The logic for this is relies on finding a directional line for the mouse click and then using it&#8217;s angle with respect the line to the periapsis of the orbit (where phase=0)<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>obtain the mouse click in screen space coordinates<\/li><li>map the orbit center (using the transform position directly since this is the 3D world position) into screen space using the Camera WorldToScreenPoint() method<\/li><li>use these to determine a directional vector of the line from the mouse click to the orbit center in screen space<\/li><li>determine a periapsis line in screen space by taking the position of phase=0 mapped to screen space with respect to the center body position in screen space<\/li><li>subtract the mouse phase from the reference phase<\/li><\/ul>\n\n\n\n<p>Note that the use of Atan2 avoid the need for quadrant checks for these angles. <\/p>\n\n\n\n<p>The corresponding code is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Vector3 mousePos = Input.mousePosition;\nVector3 originPos = orbitU.centerNbody.transform.position;\nVector3 origin = sceneCamera.WorldToScreenPoint(originPos);\nrelativeMousePos = (mousePos - origin).normalized;\n\/\/ peri: Need to map peri position onto the screen via world position. \n\/\/ (peri position includes center body offset)\nVector3 periPos = ge.MapPhyPosToWorld( orbitU.PositionForPhase(0f));\nVector3 periLine = (sceneCamera.WorldToScreenPoint(periPos) - origin).normalized;\nfloat periPhase = Mathf.Atan2(periLine.y, periLine.x) * Mathf.Rad2Deg;\nmousePhase = Mathf.Atan2(relativeMousePos.y, relativeMousePos.x) * Mathf.Rad2Deg - periPhase;\n<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The OrbitPoint component places the attached game object or NBody at a specific location on an existing orbit. Uses include showing a label for apoapsis and periapsis or indicating a position of a future course change. This video demonstrates the uses of this component: The choices for the location of an OrbitPoint are: APOAPSIS: Farthest [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":250,"menu_order":3,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"jetpack_post_was_ever_published":false,"footnotes":""},"class_list":["post-1190","page","type-page","status-publish","hentry"],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/P3pCRa-jc","_links":{"self":[{"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/pages\/1190","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/comments?post=1190"}],"version-history":[{"count":11,"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/pages\/1190\/revisions"}],"predecessor-version":[{"id":1263,"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/pages\/1190\/revisions\/1263"}],"up":[{"embeddable":true,"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/pages\/250"}],"wp:attachment":[{"href":"https:\/\/nbodyphysics.com\/blog\/wp-json\/wp\/v2\/media?parent=1190"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}