|
1 | 1 | import numpy as np |
2 | | - |
| 2 | +from famodel.helpers import calc_midpoint |
3 | 3 | ''' |
4 | 4 | famodel_base contains base classes that can be used for various classes |
5 | 5 | in the floating array model that relate to entities that act like either |
@@ -244,9 +244,19 @@ def attach(self, object, r_rel=[0,0], end=None): |
244 | 244 | ''' |
245 | 245 | # Make sure it's not already attached (note this doesn't distinguish end A/B) |
246 | 246 | if object.id in self.attachments: |
247 | | - return # for bridles, the mooring will already be attached to platform |
| 247 | + # for bridles, the mooring will already be attached to platform |
248 | 248 | # for second bridle section |
249 | | - # raise Exception(f"Object {object.id} is already attached to {self.id}") |
| 249 | + # need to calculate new r_rel that is average of end points |
| 250 | + if isinstance(object, Edge): |
| 251 | + # pull out relative dist of each point on end to self |
| 252 | + r_rel = self.calculate_r_rel(object,end=end) |
| 253 | + self.attachments[object.id]['r_rel'] = r_rel |
| 254 | + # update end position |
| 255 | + Node.setPosition(self, r=self.r,theta=self.theta) |
| 256 | + # don't need to attach, already attached- just return |
| 257 | + return |
| 258 | + else: |
| 259 | + raise Exception(f"Object {object.id} is already attached to {self.id}") |
250 | 260 |
|
251 | 261 |
|
252 | 262 | # Attach the object |
@@ -432,9 +442,9 @@ def setPosition(self, r, theta=0, force=False): |
432 | 442 | raise Exception("Can't setPosition of an object that's part of a higher object unless force=True.") |
433 | 443 |
|
434 | 444 | # Store updated position and orientation |
435 | | - if len(r) > len(self.r): |
| 445 | + if len(r) > len(self.r): # default r is 2D, but can be adjusted to 3D |
436 | 446 | self.r = np.array(r) |
437 | | - else: |
| 447 | + else: # if just a portion of r is being adjusted, only change up to length of initial r |
438 | 448 | self.r[:len(r)] = r |
439 | 449 |
|
440 | 450 | self.theta = theta |
@@ -478,6 +488,63 @@ def setPosition(self, r, theta=0, force=False): |
478 | 488 | elif isinstance(att['obj'], Edge): |
479 | 489 | att['obj'].setEndPosition(r_att, att['end']) |
480 | 490 |
|
| 491 | + def calculate_r_rel(self,object, end=None): |
| 492 | + '''Calculate the relative distance between node and object |
| 493 | + based on the combined relative distances of the subordinate/ |
| 494 | + subcomponent nodes connecting them''' |
| 495 | + if isinstance(object,Edge): |
| 496 | + # pull out subcomponent(s) attached to self at the correct end |
| 497 | + end = endToIndex(end) # find end |
| 498 | + subs = object.subcons_A if end==0 else object.subcons_B |
| 499 | + |
| 500 | + # go through all end subcomponents of edge at the correct end |
| 501 | + rel_locs = [] # relative location list (in case multiple points at end) |
| 502 | + for sub in subs: |
| 503 | + # first check if subordinate/subcomponent joined |
| 504 | + att = [att for att in sub.attachments.values() if att['obj'].attached_to==self] |
| 505 | + if len(att)>0: |
| 506 | + # find attachment of sub that is subordinately connected to self (Node) |
| 507 | + att = att[0] # just need 1st entry |
| 508 | + r_rel_att_self = self.attachments[att['id']]['r_rel'] |
| 509 | + r_rel_att_sub = att['obj'].attachments[sub.id]['r_rel'] |
| 510 | + # r_rel of sub to self is r_rel of attachment to self + r_rel of sub to attachment |
| 511 | + if len(r_rel_att_self) < 3: # pad as needed |
| 512 | + r_rel_att_self = np.hstack([r_rel_att_self,[0]]) |
| 513 | + if len(r_rel_att_sub) < 3: # pad as needed |
| 514 | + r_rel_att_sub = np.hstack([r_rel_att_sub,[0]]) |
| 515 | + rel_locs.append(r_rel_att_self + r_rel_att_sub) |
| 516 | + # otherwise, check if directly connected |
| 517 | + elif self.isAttached(object): |
| 518 | + # if no subordinate/subcomponent connection, should be |
| 519 | + # only 1 attachment point at this end |
| 520 | + return(self.attachments[object.id]['r_rel']) |
| 521 | + else: |
| 522 | + raise Exception(f'Cannot determine how {self.id} and {object.id} are connected') |
| 523 | + return calc_midpoint(rel_locs) |
| 524 | + elif isinstance(object, Node): |
| 525 | + # node to node - check if 2 subordinates connected |
| 526 | + att = [att for att in object.attachments.values() if self.isAttached(att['obj'])] |
| 527 | + if len(att)>0: |
| 528 | + att = att[0] # just need 1st entry |
| 529 | + # get relative distance of subordinately attached nodes |
| 530 | + r_rel_att_self = self.attachments[att['id']]['r_rel'] |
| 531 | + r_rel_att_obj = object.attachments[att['id']]['r_rel'] |
| 532 | + # r_rel of obj to self is r_rel of attachment to self + r_rel of obj to attachment |
| 533 | + if len(r_rel_att_self) < 3: # pad as needed |
| 534 | + r_rel_att_self = np.hstack(r_rel_att_self,[0]) |
| 535 | + if len(r_rel_att_obj) < 3: # pad as needed |
| 536 | + r_rel_att_sub = np.hstack(r_rel_att_sub,[0]) |
| 537 | + return(r_rel_att_self + r_rel_att_sub) |
| 538 | + # otherwise see if they are directly attached and return r_rel |
| 539 | + elif self.isattached(object): |
| 540 | + return self.attachments[object.id]['r_rel'] |
| 541 | + else: |
| 542 | + raise Exception(f'Cannot determine how {self.id} and {object.id} are connected') |
| 543 | + else: |
| 544 | + raise Exception(f'{object} is not a Node or Edge') |
| 545 | + |
| 546 | + |
| 547 | + |
481 | 548 |
|
482 | 549 |
|
483 | 550 |
|
@@ -1164,13 +1231,11 @@ def assemble(items): |
1164 | 1231 | return |
1165 | 1232 | ''' |
1166 | 1233 | n = len(items) |
1167 | | - |
1168 | 1234 | for i in range(n-1): |
1169 | | - |
1170 | 1235 | if isinstance(items[i], list): |
1171 | 1236 | for subitem in items[i]: # go through each parallel subitem |
1172 | | - |
1173 | 1237 | if isinstance(subitem, list): # if it's a concatenation of multiple things |
| 1238 | + |
1174 | 1239 | assemble(subitem) # make sure that any sublist is assembled |
1175 | 1240 |
|
1176 | 1241 | # attach the end objects of the subitem to the nodes before and after |
@@ -1202,7 +1267,31 @@ def assemble(items): |
1202 | 1267 |
|
1203 | 1268 | else: |
1204 | 1269 | raise Exception('sequences is not alternating between nodes and edges') |
1205 | | - |
| 1270 | + # check if last item in items is a list (if length of items>1) |
| 1271 | + # if it is a list, it won't have been attached/assembled previously, so |
| 1272 | + # attach and assemble now |
| 1273 | + if n-1>0: |
| 1274 | + if isinstance(items[i+1], list): |
| 1275 | + for subitem in items[i+1]: # go through each parallel subitem |
| 1276 | + if isinstance(subitem, list): # if it's a concatenation of multiple things |
| 1277 | + assemble(subitem) # make sure that any sublist is assembled |
| 1278 | + |
| 1279 | + # attach the end objects of the subitem to the nodes before and after |
| 1280 | + if i > 0 and isinstance(items[i], Node): # attach to previous node |
| 1281 | + items[i].attach(subitem[0], end='a') |
| 1282 | + if i < n-1 and isinstance(items[i+1], Node): # attach to next node |
| 1283 | + items[i+1].attach(subitem[-1], end='b') |
| 1284 | + # note: this requires the end objects to be edges |
| 1285 | + |
| 1286 | + elif isinstance(subitem, Edge): # if the subitem is just one edge |
| 1287 | + print("THIS CASE SHOULDN'T HAPPEN - the list should be nested more") |
| 1288 | + breakpoint() |
| 1289 | + if i > 0 and isinstance(items[i], Node): # attach to previous node |
| 1290 | + items[i].attach(subitem, end='a') |
| 1291 | + if i < n-1 and isinstance(items[i+1], Node): # attach to next node |
| 1292 | + items[i+1].attach(subitem, end='b') |
| 1293 | + else: |
| 1294 | + raise Exception("Unsupported situation ... parallel subitems must be edges or concatenations") |
1206 | 1295 |
|
1207 | 1296 | def rotationMatrix(x3,x2,x1): |
1208 | 1297 | '''Calculates a rotation matrix based on order-z,y,x instrinsic (tait-bryan?) angles, meaning |
|
0 commit comments