2020 Shape ,
2121 ShapeDict ,
2222 Space ,
23+ State ,
2324 StateDict ,
2425 _vmap_dot ,
2526 empty ,
27+ normalize ,
2628)
2729from phyjax2d .vec2d import Vec2d
2830
@@ -517,6 +519,32 @@ def make_square_segments(
517519 return lines
518520
519521
522+ def _circle_polygon_overlap (
523+ polygon : Polygon ,
524+ pstate : State ,
525+ xy : jax .Array ,
526+ radius : jax .Array | float ,
527+ ) -> jax .Array :
528+ n_batch , n_vertices = polygon .points .shape [:2 ]
529+ # Suppose that pstate.p.xy.shape == (N, 2) and xy.shape == (2,)
530+ cxy = pstate .p .inv_transform (jnp .expand_dims (xy , axis = 0 ))
531+ p2cxy = jnp .expand_dims (cxy , axis = 1 ) - polygon .points
532+ separation = _vmap_dot (polygon .normals , (p2cxy - polygon .points )) # (N, NP)
533+ max_sep = jnp .max (separation , axis = 1 )
534+ i1 = jnp .argmax (separation , axis = 1 )
535+ i2 = (i1 + 1 ) % n_vertices
536+ select_all = jnp .arange (n_batch )
537+ v1 = polygon .points [select_all , i1 ]
538+ v2 = polygon .points [select_all , i2 ]
539+ u1 = _vmap_dot (cxy - v1 , v2 - v1 )
540+ u2 = _vmap_dot (cxy - v2 , v1 - v2 )
541+ v = jnp .where (u1 < 0.0 , v1 , v2 )
542+ _ , dist = normalize (cxy - v , axis = 1 )
543+ c_out = dist < polygon .radius + radius
544+ c_in = max_sep < polygon .radius + radius
545+ return jax .lax .select (jnp .logical_or (u1 < 0.0 , u2 < 0.0 ), c_out , c_in )
546+
547+
520548def circle_overlap (
521549 shaped : ShapeDict ,
522550 stated : StateDict ,
@@ -542,6 +570,23 @@ def circle_overlap(
542570 has_overlap = jnp .logical_and (stated .static_circle .is_active , penetration >= 0 )
543571 overlap = jnp .logical_or (jnp .any (has_overlap ), overlap )
544572
573+ # Circle-segment overlap
574+ if stated .segment is not None and shaped .segment is not None :
575+ spos = stated .segment .p
576+ # Suppose that spos.shape == (N, 2) and xy.shape == (2,)
577+ pb = spos .inv_transform (jnp .expand_dims (xy , axis = 0 ))
578+ p1 , p2 = shaped .segment .point1 , shaped .segment .point2
579+ edge = p2 - p1
580+ s1 = jnp .expand_dims (_vmap_dot (pb - p1 , edge ), axis = 1 )
581+ s2 = jnp .expand_dims (_vmap_dot (p2 - pb , edge ), axis = 1 )
582+ in_segment = jnp .logical_and (s1 >= 0.0 , s2 >= 0.0 )
583+ ee = jnp .sum (jnp .square (edge ), axis = - 1 , keepdims = True )
584+ pa = jnp .where (in_segment , p1 + edge * s1 / ee , jnp .where (s1 < 0.0 , p1 , p2 ))
585+ dist = jnp .linalg .norm (pb - pa , axis = - 1 )
586+ penetration = radius - dist
587+ has_overlap = jnp .logical_and (stated .segment .is_active , penetration >= 0 )
588+ overlap = jnp .logical_or (jnp .any (has_overlap ), overlap )
589+
545590 # Circle-segment overlap
546591 if stated .segment is not None and shaped .segment is not None :
547592 spos = stated .segment .p
@@ -559,4 +604,32 @@ def circle_overlap(
559604 has_overlap = jnp .logical_and (stated .segment .is_active , penetration >= 0 )
560605 overlap = jnp .logical_or (jnp .any (has_overlap ), overlap )
561606
607+ # Circle-polygon overlap
608+ if stated .triangle is not None and shaped .trianlge is not None :
609+ has_overlap = _circle_polygon_overlap (
610+ shaped .triangle ,
611+ stated .triangle ,
612+ xy ,
613+ radius ,
614+ )
615+ overlap = jnp .logical_or (jnp .any (has_overlap ), overlap )
616+
617+ if stated .quadrangle is not None and shaped .quadrangle is not None :
618+ has_overlap = _circle_polygon_overlap (
619+ shaped .quadrangle ,
620+ stated .quadrangle ,
621+ xy ,
622+ radius ,
623+ )
624+ overlap = jnp .logical_or (jnp .any (has_overlap ), overlap )
625+
626+ if stated .pentagon is not None and shaped .pentagon is not None :
627+ has_overlap = _circle_polygon_overlap (
628+ shaped .pentagon ,
629+ stated .pentagon ,
630+ xy ,
631+ radius ,
632+ )
633+ overlap = jnp .logical_or (jnp .any (has_overlap ), overlap )
634+
562635 return overlap
0 commit comments