1 /* -------------------------------------------------------------------------- *
2  *                               Simbody(tm)                                  *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2014 Stanford University and the Authors.           *
10  * Authors: Michael Sherman                                                   *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 /* Implementation of non-inline methods of the handle class
25 Constraint::LineOnLineContact, and its implementation class
26 Constraint::LineOnLineContactImpl. */
27 
28 #include "SimTKcommon.h"
29 #include "simbody/internal/common.h"
30 #include "simbody/internal/Constraint.h"
31 #include "simbody/internal/Constraint_LineOnLineContact.h"
32 
33 #include "Constraint_LineOnLineContactImpl.h"
34 #include "SimbodyMatterSubsystemRep.h"
35 
36 namespace SimTK {
37 
38 
39 //==============================================================================
40 //                         LINE ON LINE CONTACT
41 //==============================================================================
42 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Constraint::LineOnLineContact,
43                                         Constraint::LineOnLineContactImpl,
44                                         Constraint);
45 
LineOnLineContact(MobilizedBody & mobod_F,const Transform & defaultEdgeFrameF,Real defaultHalfLengthF,MobilizedBody & mobod_B,const Transform & defaultEdgeFrameB,Real defaultHalfLengthB,bool enforceRolling)46 Constraint::LineOnLineContact::LineOnLineContact
47    (MobilizedBody&      mobod_F,
48     const Transform&    defaultEdgeFrameF,
49     Real                defaultHalfLengthF,
50     MobilizedBody&      mobod_B,
51     const Transform&    defaultEdgeFrameB,
52     Real                defaultHalfLengthB,
53     bool                enforceRolling)
54 :   Constraint(new LineOnLineContactImpl(enforceRolling))
55 {
56     SimTK_APIARGCHECK_ALWAYS(mobod_F.isInSubsystem() && mobod_B.isInSubsystem(),
57         "Constraint::LineOnLineContact","LineOnLineContact",
58         "Both mobilized bodies must already be in a SimbodyMatterSubsystem.");
59     SimTK_APIARGCHECK_ALWAYS(mobod_F.isInSameSubsystem(mobod_B),
60         "Constraint::LineOnLineContact","LineOnLineContact",
61         "The two mobilized bodies to be connected must be in the same "
62         "SimbodyMatterSubsystem.");
63 
64     mobod_F.updMatterSubsystem().adoptConstraint(*this);
65 
66     updImpl().m_mobod_F     = updImpl().addConstrainedBody(mobod_F);
67     updImpl().m_mobod_B     = updImpl().addConstrainedBody(mobod_B);
68     updImpl().m_def_X_FEf   = defaultEdgeFrameF;
69     updImpl().m_def_hf      = defaultHalfLengthF;
70     updImpl().m_def_X_BEb   = defaultEdgeFrameB;
71     updImpl().m_def_hb      = defaultHalfLengthB;
72 }
73 
74 Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setDefaultEdgeFrameF(const Transform & defaultEdgeFrameF)75 setDefaultEdgeFrameF(const Transform& defaultEdgeFrameF) {
76     getImpl().invalidateTopologyCache();
77     updImpl().m_def_X_FEf = defaultEdgeFrameF;
78     return *this;
79 }
80 
81 Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setDefaultHalfLengthF(Real defaultHalfLengthF)82 setDefaultHalfLengthF(Real defaultHalfLengthF) {
83     getImpl().invalidateTopologyCache();
84     updImpl().m_def_hf = defaultHalfLengthF;
85     return *this;
86 }
87 
88 Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setDefaultEdgeFrameB(const Transform & defaultEdgeFrameB)89 setDefaultEdgeFrameB(const Transform& defaultEdgeFrameB) {
90     getImpl().invalidateTopologyCache();
91     updImpl().m_def_X_BEb = defaultEdgeFrameB;
92     return *this;
93 }
94 
95 Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setDefaultHalfLengthB(Real defaultHalfLengthB)96 setDefaultHalfLengthB(Real defaultHalfLengthB) {
97     getImpl().invalidateTopologyCache();
98     updImpl().m_def_hb = defaultHalfLengthB;
99     return *this;
100 }
101 
102 const MobilizedBody& Constraint::LineOnLineContact::
getMobilizedBodyF() const103 getMobilizedBodyF() const {
104     const LineOnLineContactImpl& impl = getImpl();
105     return impl.getMobilizedBodyFromConstrainedBody(impl.m_mobod_F);
106 }
107 const MobilizedBody& Constraint::LineOnLineContact::
getMobilizedBodyB() const108 getMobilizedBodyB() const {
109     const LineOnLineContactImpl& impl = getImpl();
110     return impl.getMobilizedBodyFromConstrainedBody(impl.m_mobod_B);
111 }
112 
isEnforcingRolling() const113 bool Constraint::LineOnLineContact::isEnforcingRolling() const
114 {   return getImpl().m_enforceRolling; }
115 
116 const Transform& Constraint::LineOnLineContact::
getDefaultEdgeFrameF() const117 getDefaultEdgeFrameF() const {return getImpl().m_def_X_FEf;}
118 
119 Real Constraint::LineOnLineContact::
getDefaultHalfLengthF() const120 getDefaultHalfLengthF() const {return getImpl().m_def_hf;}
121 
122 const Transform& Constraint::LineOnLineContact::
getDefaultEdgeFrameB() const123 getDefaultEdgeFrameB() const {return getImpl().m_def_X_BEb;}
124 
125 Real Constraint::LineOnLineContact::
getDefaultHalfLengthB() const126 getDefaultHalfLengthB() const {return getImpl().m_def_hb;}
127 
128 const Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setEdgeFrameF(State & state,const Transform & edgeFrameF) const129 setEdgeFrameF(State& state, const Transform& edgeFrameF) const {
130     getImpl().updParameters(state).X_FEf = edgeFrameF;
131     return *this;
132 }
133 
134 const Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setHalfLengthF(State & state,Real halfLengthF) const135 setHalfLengthF(State& state, Real halfLengthF) const {
136     getImpl().updParameters(state).hf = halfLengthF;
137     return *this;
138 }
139 
140 const Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setEdgeFrameB(State & state,const Transform & edgeFrameB) const141 setEdgeFrameB(State& state, const Transform& edgeFrameB) const {
142     getImpl().updParameters(state).X_BEb = edgeFrameB;
143     return *this;
144 }
145 
146 const Constraint::LineOnLineContact& Constraint::LineOnLineContact::
setHalfLengthB(State & state,Real halfLengthB) const147 setHalfLengthB(State& state, Real halfLengthB) const {
148     getImpl().updParameters(state).hb = halfLengthB;
149     return *this;
150 }
151 const Transform& Constraint::LineOnLineContact::
getEdgeFrameF(const State & state) const152 getEdgeFrameF(const State& state) const
153 {   return getImpl().getParameters(state).X_FEf; }
154 Real Constraint::LineOnLineContact::
getHalfLengthF(const State & state) const155 getHalfLengthF(const State& state) const
156 {   return getImpl().getParameters(state).hf; }
157 const Transform& Constraint::LineOnLineContact::
getEdgeFrameB(const State & state) const158 getEdgeFrameB(const State& state) const
159 {   return getImpl().getParameters(state).X_BEb; }
160 Real Constraint::LineOnLineContact::
getHalfLengthB(const State & state) const161 getHalfLengthB(const State& state) const
162 {   return getImpl().getParameters(state).hb; }
163 
getPositionError(const State & s) const164 Real Constraint::LineOnLineContact::getPositionError(const State& s) const {
165     Real perr;
166     getImpl().getPositionErrors(s, 1, &perr);
167     return perr;
168 }
169 
getVelocityErrors(const State & s) const170 Vec3 Constraint::LineOnLineContact::getVelocityErrors(const State& s) const {
171     const LineOnLineContactImpl& impl = getImpl();
172     Vec3 verr_PC; // result is velocity error in P frame
173     if (impl.m_enforceRolling) {
174         Real verr[3];
175         impl.getVelocityErrors(s, 3, verr);
176         verr_PC = Vec3(verr[1],verr[2],verr[0]); // switch to x,y,z order
177     } else {
178         Real pverr;
179         getImpl().getVelocityErrors(s, 1, &pverr);
180         verr_PC = Vec3(0,0,pverr); // lone error is in z direction
181     }
182     return verr_PC;
183 }
184 
getAccelerationErrors(const State & s) const185 Vec3 Constraint::LineOnLineContact::getAccelerationErrors(const State& s) const {
186     const LineOnLineContactImpl& impl = getImpl();
187     Vec3 aerr_PC; // result is acceleration error in P frame
188     if (impl.m_enforceRolling) {
189         Real aerr[3];
190         impl.getAccelerationErrors(s, 3, aerr);
191         aerr_PC = Vec3(aerr[1],aerr[2],aerr[0]); // switch to x,y,z order
192     } else {
193         Real paerr;
194         getImpl().getAccelerationErrors(s, 1, &paerr);
195         aerr_PC = Vec3(0,0,paerr); // lone error is in z direction
196     }
197     return aerr_PC;
198 }
199 
getMultipliers(const State & s) const200 Vec3 Constraint::LineOnLineContact::getMultipliers(const State& s) const {
201     const LineOnLineContactImpl& impl = getImpl();
202     Vec3 lambda_PC; // result is -force on point F in P frame
203     if (impl.m_enforceRolling) {
204         Real lambda[3];
205         impl.getMultipliers(s, 3, lambda);
206         lambda_PC = Vec3(lambda[1],lambda[2],lambda[0]); //switch to x,y,z order
207     } else {
208         Real lambda;
209         getImpl().getMultipliers(s, 1, &lambda);
210         lambda_PC = Vec3(0,0,lambda); // lone force is in z direction
211     }
212     return lambda_PC;
213 }
214 
215 Vec3 Constraint::LineOnLineContact::
findForceOnBodyBInG(const State & s) const216 findForceOnBodyBInG(const State& s) const {
217     const LineOnLineContactImpl& impl = getImpl();
218     if (impl.isDisabled(s))
219         return Vec3(0);
220 
221     const Transform X_GC = findContactFrameInG(s);
222 
223     const Vec3 f_C = -getMultipliers(s); // watch sign convention
224     return X_GC.R()*f_C;
225 }
226 
227 Transform Constraint::LineOnLineContact::
findContactFrameInG(const State & s) const228 findContactFrameInG(const State& s) const {
229     const LineOnLineContactImpl& impl = getImpl();
230     const LineOnLineContactImpl::PositionCache& pc =
231         impl.ensurePositionCacheRealized(s);
232 
233     const MobilizedBody& mobod_A = impl.getAncestorMobilizedBody();
234     if (mobod_A.isGround())
235         return pc.X_AC; // == X_GC
236 
237     const Transform& X_GA = mobod_A.getBodyTransform(s);
238     return X_GA * pc.X_AC;  // 63 flops
239 }
240 
241 void Constraint::LineOnLineContact::
findClosestPointsInG(const State & s,Vec3 & Qf_G,Vec3 & Qb_G,bool & linesAreParallel) const242 findClosestPointsInG(const State& s, Vec3& Qf_G, Vec3& Qb_G,
243                      bool& linesAreParallel) const {
244     const LineOnLineContactImpl& impl = getImpl();
245     const LineOnLineContactImpl::PositionCache& pc =
246         impl.ensurePositionCacheRealized(s);
247 
248     const MobilizedBody& mobod_A = impl.getAncestorMobilizedBody();
249     if (mobod_A.isGround()) {
250         Qf_G = pc.p_AQf;
251         Qb_G = pc.p_AQb;
252     } else {
253         const Transform& X_GA = mobod_A.getBodyTransform(s);
254         Qf_G = X_GA * pc.p_AQf; // 18 flops
255         Qb_G = X_GA * pc.p_AQb; // 18 flops
256     }
257 }
258 
259 // The separation is the signed distance between the lines' closest points.
260 // Same as perr routine, but works when constraint is disabled.
261 Real Constraint::LineOnLineContact::
findSeparation(const State & s) const262 findSeparation(const State& s) const {
263     const LineOnLineContactImpl::PositionCache& pc =
264         getImpl().ensurePositionCacheRealized(s);
265 
266     const Real r = ~pc.p_PfPb_A * pc.n_A; // 5 flops
267     return r;
268 }
269 
270 
271 //==============================================================================
272 //                        LINE ON LINE CONTACT IMPL
273 //==============================================================================
274 
275 // The default parameters may be overridden by setting a discrete variable in
276 // the state, and we need a couple of cache entries to hold expensive
277 // computations. We allocate the state resources here.
278 void Constraint::LineOnLineContactImpl::
realizeTopologyVirtual(State & state) const279 realizeTopologyVirtual(State& state) const {
280     m_parametersIx = getMyMatterSubsystemRep().
281         allocateDiscreteVariable(state, Stage::Position,
282             new Value<Parameters>(Parameters(m_def_X_FEf, m_def_hf,
283                                              m_def_X_BEb, m_def_hb)));
284 
285     m_posCacheIx = getMyMatterSubsystemRep().
286         allocateLazyCacheEntry(state, Stage::Position,
287             new Value<PositionCache>());
288 
289     m_velCacheIx = getMyMatterSubsystemRep().
290         allocateLazyCacheEntry(state, Stage::Velocity,
291             new Value<VelocityCache>());
292 }
293 
294 const Constraint::LineOnLineContactImpl::Parameters&
295 Constraint::LineOnLineContactImpl::
getParameters(const State & state) const296 getParameters(const State& state) const {
297     return Value<Parameters>::downcast
298        (getMyMatterSubsystemRep().getDiscreteVariable(state,m_parametersIx));
299 }
300 
301 Constraint::LineOnLineContactImpl::Parameters&
302 Constraint::LineOnLineContactImpl::
updParameters(State & state) const303 updParameters(State& state) const {
304     return Value<Parameters>::updDowncast
305        (getMyMatterSubsystemRep().updDiscreteVariable(state,m_parametersIx));
306 }
307 
308 const Constraint::LineOnLineContactImpl::PositionCache&
309 Constraint::LineOnLineContactImpl::
getPositionCache(const State & state) const310 getPositionCache(const State& state) const {
311     return Value<PositionCache>::downcast
312        (getMyMatterSubsystemRep().getCacheEntry(state,m_posCacheIx));
313 }
314 
315 Constraint::LineOnLineContactImpl::PositionCache&
316 Constraint::LineOnLineContactImpl::
updPositionCache(const State & state) const317 updPositionCache(const State& state) const {
318     return Value<PositionCache>::updDowncast
319        (getMyMatterSubsystemRep().updCacheEntry(state,m_posCacheIx));
320 }
321 
322 const Constraint::LineOnLineContactImpl::VelocityCache&
323 Constraint::LineOnLineContactImpl::
getVelocityCache(const State & state) const324 getVelocityCache(const State& state) const {
325     return Value<VelocityCache>::downcast
326        (getMyMatterSubsystemRep().getCacheEntry(state,m_velCacheIx));
327 }
328 
329 Constraint::LineOnLineContactImpl::VelocityCache&
330 Constraint::LineOnLineContactImpl::
updVelocityCache(const State & state) const331 updVelocityCache(const State& state) const {
332     return Value<VelocityCache>::updDowncast
333        (getMyMatterSubsystemRep().updCacheEntry(state,m_velCacheIx));
334 }
335 
336 // This costs about 213 flops.
337 void Constraint::LineOnLineContactImpl::
calcPositionInfo(const State & state,const Transform & X_AF,const Transform & X_AB,PositionCache & pc) const338 calcPositionInfo(const State& state,
339                  const Transform& X_AF, const Transform& X_AB,
340                  PositionCache& pc) const
341 {
342     const Parameters& params = getParameters(state);
343     const UnitVec3&     df_F   = params.X_FEf.x();
344     const UnitVec3&     db_B   = params.X_BEb.x();
345     const UnitVec3&     sf_F   = params.X_FEf.z(); // outward normal
346     const UnitVec3&     sb_B   = params.X_BEb.z();
347     const Vec3&         p_FPf  = params.X_FEf.p();
348     const Vec3&         p_BPb  = params.X_BEb.p();
349 
350     pc.df_A     = X_AF.R() * df_F;                  // 15 flops
351     pc.db_A     = X_AB.R() * db_B;                  // 15
352     pc.p_FPf_A  = X_AF.R() * p_FPf;                 // 15
353     pc.p_BPb_A  = X_AB.R() * p_BPb;                 // 15
354     const Vec3 p_APf = X_AF.p() + pc.p_FPf_A;       //  3
355     const Vec3 p_APb = X_AB.p() + pc.p_BPb_A;       //  3
356     pc.p_PfPb_A = p_APb - p_APf;                    //  3
357 
358     const UnitVec3 sf_A = X_AF.R() * sf_F;          // 15 flops
359     const UnitVec3 sb_A = X_AB.R() * sb_B;          // 15
360     const Vec3 wraw_A = pc.df_A % pc.db_A;          //  9
361 
362     // Use whichever of the outward normal directions gives a clearer signal.
363     // We want w point out from F or into B.          ~12 flops
364     const Real wsf = ~wraw_A*sf_A, wsb = ~wraw_A*sb_A;
365     if (std::abs(wsf) > std::abs(wsb)) pc.sense = (wsf >= 0 ?  1 : -1);
366     else                               pc.sense = (wsb >= 0 ? -1 :  1);
367 
368     pc.w_A = pc.sense * wraw_A;                     //  3 flops
369     const Real sinTheta = pc.w_A.norm();            //~20
370 
371     //TODO: should do something better than this for parallel edges. Not clear
372     // that an exception is best though since this might occur prior to an
373     // assembly analysis that would fix the problem. Also consider if we're
374     // doing event-driven contact we may need to catch the event of edges
375     // going from crossed to parallel to turn off the edge/edge contact.
376 
377     if (sinTheta < SignificantReal) {               // 1 flop
378         pc.edgesAreParallel = true;
379         pc.oos = 1/SignificantReal; // just to avoid NaN-ing if sinTheta==0
380     } else {
381         pc.edgesAreParallel = false;
382         pc.oos = 1/sinTheta;                        //~10
383     }
384 
385     pc.n_A = UnitVec3(pc.w_A * pc.oos, true);       //  3
386 
387     pc.pXn = pc.p_PfPb_A % pc.n_A;                  //  9 flops
388 
389     const Real f = -pc.sense*pc.oos;                //  2 flops
390     pc.tf = f*dot(pc.db_A, pc.pXn);                 //  6
391     pc.tb = f*dot(pc.df_A, pc.pXn);                 //  6
392 
393     // Create the contact frame C. The origin should be half way between
394     // Qf and Qb. The z axis is the contact normal n. x is along edge Ef, in
395     // direction df. y = z X x = n X df.
396     pc.p_AQf = p_APf + pc.tf * pc.df_A;             //  6 flops
397     pc.p_AQb = p_APb + pc.tb * pc.db_A;             //  6
398     const Vec3 p_ACo = 0.5*(pc.p_AQf + pc.p_AQb);   //  6
399     pc.X_AC.updP() = p_ACo;
400 
401     // Since unit vector n is perpendicular to df (and db), n X df
402     // is already a unit vector without normalizing.
403     const UnitVec3 nXdf(pc.n_A % pc.df_A, true);    //  9 flops
404     pc.X_AC.updR().setRotationFromUnitVecsTrustMe(pc.df_A, nXdf, pc.n_A);
405 
406     // Vectors from F and B body origins to contact point, expressed in A.
407     // These are the station locations at which forces are applied.
408     pc.p_FCo_A = p_ACo - X_AF.p();                  //  3 flops
409     pc.p_BCo_A = p_ACo - X_AB.p();                  //  3
410 }
411 
412 
413 const Constraint::LineOnLineContactImpl::PositionCache&
414 Constraint::LineOnLineContactImpl::
ensurePositionCacheRealized(const State & s) const415 ensurePositionCacheRealized(const State& s) const {
416     if (getMyMatterSubsystemRep().isCacheValueRealized(s, m_posCacheIx))
417         return getPositionCache(s);
418     PositionCache& pc = updPositionCache(s);
419 
420     const Transform& X_AF = getBodyTransformFromState(s, m_mobod_F);
421     const Transform& X_AB = getBodyTransformFromState(s, m_mobod_B);
422 
423     calcPositionInfo(s, X_AF, X_AB, pc);
424 
425     getMyMatterSubsystemRep().markCacheValueRealized(s, m_posCacheIx);
426     return pc;
427 }
428 
429 
430 // This costs about 340 flops if position info has already been calculated,
431 // otherwise we also pay for ensurePositionCacheRealized().
432 void Constraint::LineOnLineContactImpl::
calcVelocityInfo(const State & state,const SpatialVec & V_AF,const SpatialVec & V_AB,VelocityCache & vc) const433 calcVelocityInfo(const State& state,
434                  const SpatialVec& V_AF, const SpatialVec& V_AB,
435                  VelocityCache& vc) const
436 {
437     const PositionCache& pc = ensurePositionCacheRealized(state);
438     if (pc.edgesAreParallel)
439         return;
440 
441     const Vec3& w_AF = V_AF[0]; // handy abbreviations
442     const Vec3& v_AF = V_AF[1];
443     const Vec3& w_AB = V_AB[0];
444     const Vec3& v_AB = V_AB[1];
445 
446     // These are d/dt_A p_FPf and d/dt_A p_BPb
447     const Vec3 wX_p_FPf_A = w_AF % pc.p_FPf_A;                  //  9 flops
448     const Vec3 wX_p_BPb_A = w_AB % pc.p_BPb_A;                  //  9
449     const Vec3 v_APf = v_AF + wX_p_FPf_A;                       //  3
450     const Vec3 v_APb = v_AB + wX_p_BPb_A;                       //  3
451     vc.dp_PfPb_A = v_APb - v_APf;                               //  3
452 
453     vc.ddf_A = w_AF % pc.df_A;                                  //  9 flops
454     vc.ddb_A = w_AB % pc.db_A;                                  //  9
455     vc.dw_A = pc.sense*(vc.ddf_A % pc.db_A + pc.df_A % vc.ddb_A);//24
456     vc.dn_A = pc.oos * (vc.dw_A - (~pc.n_A*vc.dw_A)*pc.n_A);    // 14
457 
458     // Calculate the velocity of B's material point (station) at Co,
459     // measured in the F frame and expressed in A.
460     const Vec3 vA_BCo_A = v_AB + w_AB % pc.p_BCo_A;             // 12 flops
461     const Vec3 vA_FCo_A = v_AF + w_AF % pc.p_FCo_A;             // 12
462     vc.vF_BCo_A = vA_BCo_A - vA_FCo_A;                          //  3
463 
464     // We have s=||w||, oos=1/s. We want doos = d/dt oos.
465     vc.doos = -square(pc.oos) * dot(pc.n_A, vc.dw_A);           //  8 flops
466 
467     const Vec3 nXdb = pc.n_A % pc.db_A;                         //  9 flops
468     const Vec3 nXdf = pc.n_A % pc.df_A;                         //  9
469     const Vec3 d_nXdb = vc.dn_A % pc.db_A + pc.n_A % vc.ddb_A;  // 21
470     const Vec3 d_nXdf = vc.dn_A % pc.df_A + pc.n_A % vc.ddf_A;  // 21
471     const Real dtf = -pc.sense * (                              // 20
472           pc.oos  * (~vc.dp_PfPb_A*nXdb + ~pc.p_PfPb_A*d_nXdb)
473         + vc.doos * (~pc.p_PfPb_A*nXdb) );
474     const Real dtb = -pc.sense * (                              // 20
475           pc.oos  * (~vc.dp_PfPb_A*nXdf + ~pc.p_PfPb_A*d_nXdf)
476         + vc.doos * (~pc.p_PfPb_A*nXdf) );
477 
478     const Vec3 dQf = v_APf + dtf * pc.df_A + pc.tf * vc.ddf_A;  // 12 flops
479     const Vec3 dQb = v_APb + dtb * pc.db_A + pc.tb * vc.ddb_A;  // 12
480     const Vec3 dCo = 0.5*(dQf + dQb);                           //  6
481     const Vec3 dp_FCo = dCo - v_AF;                             //  3
482     const Vec3 dp_BCo = dCo - v_AB;                             //  3
483 
484     vc.wXdp_FCo_A = w_AF % dp_FCo;                              //  9 flops
485     vc.wXdp_BCo_A = w_AB % dp_BCo;                              //  9
486     vc.ddfXddb2   = 2.*(vc.ddf_A % vc.ddb_A);                   // 12
487     vc.wXddf_A    = w_AF % vc.ddf_A;                            //  9
488     vc.wXddb_A    = w_AB % vc.ddb_A;                            //  9
489 
490     // These are the Coriolis accelerations of Pf and Pb, needed later.
491     vc.wXwX_p_FPf_A = w_AF % wX_p_FPf_A;                        //  9 flops
492     vc.wXwX_p_BPb_A = w_AB % wX_p_BPb_A;                        //  9
493 
494     // Record derivative of the contact frame.
495     // We have Cx=df, Cz=n, Cy=n x df. Want derivatives in A.
496     vc.dCx_A = vc.ddf_A;
497     vc.dCz_A = vc.dn_A;
498     vc.dCy_A = d_nXdf;
499     vc.dCo_A = dCo;
500 }
501 
502 const Constraint::LineOnLineContactImpl::VelocityCache&
503 Constraint::LineOnLineContactImpl::
ensureVelocityCacheRealized(const State & s) const504 ensureVelocityCacheRealized(const State& s) const {
505     if (getMyMatterSubsystemRep().isCacheValueRealized(s, m_velCacheIx))
506         return getVelocityCache(s);
507     VelocityCache& vc = updVelocityCache(s);
508     const SpatialVec& V_AF = getBodyVelocityFromState(s, m_mobod_F);
509     const SpatialVec& V_AB = getBodyVelocityFromState(s, m_mobod_B);
510 
511     calcVelocityInfo(s, V_AF, V_AB, vc);
512 
513     getMyMatterSubsystemRep().markCacheValueRealized(s, m_velCacheIx);
514     return vc;
515 }
516 
517 void Constraint::LineOnLineContactImpl::
calcDecorativeGeometryAndAppendVirtual(const State & s,Stage stage,Array_<DecorativeGeometry> & geom) const518 calcDecorativeGeometryAndAppendVirtual
519    (const State& s, Stage stage, Array_<DecorativeGeometry>& geom) const
520 {
521     // We can't generate the artwork until we know the lines' placements,
522     // which might not be until Position stage.
523     if (   stage != Stage::Position
524         || !getMyMatterSubsystemRep().getShowDefaultGeometry())
525         return;
526 
527     const Parameters&       params = getParameters(s);
528     const Real              hf     = params.hf;
529     const Real              hb     = params.hb;
530 
531     const PositionCache&    pc = ensurePositionCacheRealized(s);
532     const Transform&        X_AC = pc.X_AC;
533 
534     const MobilizedBody&    mobod_A = getAncestorMobilizedBody();
535     const Transform&        X_GA    = mobod_A.getBodyTransform(s);
536     const Rotation&         R_GA    = X_GA.R();
537 
538     const Transform  X_GC = X_GA * X_AC;
539 
540     // Convert interesting stuff from A to G.
541     const UnitVec3   df_G  = R_GA * X_AC.x();
542     const UnitVec3   db_G  = R_GA * pc.db_A;
543     const Vec3       p_GQf = X_GA * pc.p_AQf;
544     const Vec3       p_GQb = X_GA * pc.p_AQb;
545     const Vec3       half_Lf = hf * df_G;
546     const Vec3       half_Lb = hb * db_G;
547 
548     const MobilizedBody& bodyF = getMobilizedBodyFromConstrainedBody(m_mobod_F);
549     const MobilizedBody& bodyB = getMobilizedBodyFromConstrainedBody(m_mobod_B);
550 
551     const Transform& X_GF  = bodyF.getBodyTransform(s);
552     const Transform& X_GB  = bodyB.getBodyTransform(s);
553     const Transform  X_GEf = X_GF * params.X_FEf;
554     const Transform  X_GEb = X_GB * params.X_BEb;
555 
556 
557     // On body F draw a green line segment around the orange closest point.
558     geom.push_back(DecorativeLine(p_GQf-half_Lf, p_GQf+half_Lf)
559         .setColor(Green));
560     geom.push_back(DecorativeFrame().setTransform(X_GEf)
561         .setColor(Green*.9).setLineThickness(1).setScale(0.5)); // F color
562     geom.push_back(DecorativePoint(p_GQf)
563         .setColor(Orange).setLineThickness(2)); // B color
564 
565     // On body B draw an orange line segment around the green closest point.
566     geom.push_back(DecorativeLine(p_GQb-half_Lb, p_GQb+half_Lb)
567         .setColor(Orange));
568     geom.push_back(DecorativeFrame().setTransform(X_GEb)
569         .setColor(Orange*.9).setLineThickness(1).setScale(0.5)); // B color
570     geom.push_back(DecorativePoint(p_GQb)
571         .setColor(Green).setLineThickness(2)); // F color
572 
573     // Show the contact frame in red.
574     geom.push_back(DecorativeFrame().setTransform(X_GC)
575                    .setColor(Red));
576 }
577 
578 
579 } // namespace SimTK
580 
581