1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.frames;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitMessages;
25  
26  
27  /** Prototype frame that can be built from leaf to roots and later attached to a tree.
28   *
29   * <p>Regular {@link Frame} instances can be built only from a parent frame, i.e.
30   * the frames tree can be built only from root to leafs. In some cases, it may
31   * desirable to build a subset tree and attach it to the main tree after build
32   * time, which means the tree is built from leafs to root. This class allows
33   * building this subtree.</p>
34   * <p>
35   * During the build process, the {@link Frame} associated with each {@link OrphanFrame}
36   * is not available. It becomes available only once the {@link OrphanFrame} has been
37   * attached to the main tree, and at this time it can be used to compute
38   * {@link Transform transforms}.
39   * </p>
40   *
41   * @author Luc Maisonobe
42   * @since 6.0
43   */
44  public class OrphanFrame {
45  
46      /** Instance name. */
47      private final String name;
48  
49      /** Children of the frame. */
50      private final List<OrphanFrame> children;
51  
52      /** Parent orphan frame. */
53      private OrphanFrame orphanParent;
54  
55      /** Provider for transform from parent frame to instance. */
56      private TransformProvider provider;
57  
58      /** Indicator for pseudo-inertial frames. */
59      private boolean pseudoInertial;
60  
61      /** Associated frame (available only once attached to the main frames tree). */
62      private Frame frame;
63  
64      /** Simple constructor.
65       * @param name name of the frame
66       */
67      public OrphanFrame(final String name) {
68          children  = new ArrayList<>();
69          this.name = name;
70      }
71  
72      /** Add a child.
73       * <p>
74       * If a child is added after the instance has been attached, the child and
75       * all its tree will be attached immediately too.
76       * </p>
77       * @param child child to add
78       * @param transform transform from instance to child
79       * @param isPseudoInertial true if child is considered pseudo-inertial
80       * (i.e. suitable for propagating orbit)
81       */
82      public void addChild(final OrphanFrame child, final Transform transform,
83                           final boolean isPseudoInertial) {
84          addChild(child, new FixedTransformProvider(transform), isPseudoInertial);
85      }
86  
87      /** Add a child.
88       * <p>
89       * If a child is added after the instance has been attached, the child and
90       * all its tree will be attached immediately too.
91       * </p>
92       * @param child child to add
93       * @param transformProvider provider for transform from instance to child
94       * @param isPseudoInertial true if child is considered pseudo-inertial
95       * (i.e. suitable for propagating orbit)
96       */
97      public void addChild(final OrphanFrame child, final TransformProvider transformProvider,
98                           final boolean isPseudoInertial) {
99  
100         // safety check
101         if (child.orphanParent != null) {
102             throw new OrekitException(OrekitMessages.FRAME_ALREADY_ATTACHED,
103                                       child.name, child.orphanParent.name);
104         }
105 
106         children.add(child);
107         child.orphanParent   = this;
108         child.provider       = transformProvider;
109         child.pseudoInertial = isPseudoInertial;
110 
111         if (frame != null) {
112             // we are attaching a child after having attached the instance,
113             // we process the tree immediately
114             buildTree();
115         }
116 
117     }
118 
119     /** Attach the instance (and all its children down to leafs) to the main tree.
120      * @param parent parent frame to attach to
121      * @param transform transform from parent frame to instance
122      * @param isPseudoInertial true if frame is considered pseudo-inertial
123      * (i.e. suitable for propagating orbit)
124      */
125     public void attachTo(final Frame parent, final Transform transform,
126                          final boolean isPseudoInertial) {
127         attachTo(parent, new FixedTransformProvider(transform), isPseudoInertial);
128     }
129 
130     /** Attach the instance (and all its children down to leafs) to the main tree.
131      * @param parent parent frame to attach to
132      * @param transformProvider provider for transform from parent frame to instance
133      * @param isPseudoInertial true if frame is considered pseudo-inertial
134      * (i.e. suitable for propagating orbit)
135      */
136     public void attachTo(final Frame parent, final TransformProvider transformProvider,
137                          final boolean isPseudoInertial) {
138 
139         // safety check
140         if (orphanParent != null) {
141             throw new OrekitException(OrekitMessages.FRAME_ALREADY_ATTACHED,
142                                       name, orphanParent.name);
143         }
144 
145         // set up the attached point
146         final OrphanFrame op = new OrphanFrame(parent.getName());
147         op.frame = parent;
148         op.addChild(this, transformProvider, isPseudoInertial);
149 
150     }
151 
152     /** Get all children of the instance.
153      * @return unmodifiable list of children
154      */
155     public List<OrphanFrame> getChildren() {
156         return Collections.unmodifiableList(children);
157     }
158 
159     /** Get the associated {@link Frame frame}.
160      * @return associated frame
161      */
162     public Frame getFrame() {
163 
164         // safety check
165         if (frame == null) {
166             throw new OrekitException(OrekitMessages.FRAME_NOT_ATTACHED, name);
167         }
168 
169         return frame;
170 
171     }
172 
173     /** Recursively build the frames tree starting at instance, which is already associated.
174      */
175     private void buildTree() {
176         for (final OrphanFrame child : children) {
177 
178             if (child.frame == null) {
179 
180                 // associate the child with a regular frame
181                 child.frame = new Frame(frame, child.provider, child.name, child.pseudoInertial);
182 
183                 // recursively build the rest of the tree
184                 child.buildTree();
185 
186             }
187 
188         }
189     }
190 
191     /** {@inheritDoc} */
192     @Override
193     public String toString() {
194         return this.name;
195     }
196 
197 }