@@ -896,7 +896,7 @@ function createDerivedSketchLine(feature, candidate) {
896896 y : candidate . aLocal . y || 0 ,
897897 fixed : true ,
898898 derived : true ,
899- source : { ... ( source || { } ) , point_kind : 'a' }
899+ source : null
900900 } ;
901901 const p2 = {
902902 id : this . newSketchEntityId ( 'point' ) ,
@@ -905,7 +905,7 @@ function createDerivedSketchLine(feature, candidate) {
905905 y : candidate . bLocal . y || 0 ,
906906 fixed : true ,
907907 derived : true ,
908- source : { ... ( source || { } ) , point_kind : 'b' }
908+ source : null
909909 } ;
910910 const line = {
911911 id : this . newSketchEntityId ( 'line' ) ,
@@ -930,6 +930,129 @@ function createDerivedSketchLine(feature, candidate) {
930930 return created ;
931931}
932932
933+ function deriveSelectionsAtomic ( feature , selection = { } ) {
934+ if ( ! feature || feature . type !== 'sketch' ) return false ;
935+ const edges = Array . isArray ( selection ?. edges ) ? selection . edges : [ ] ;
936+ const points = Array . isArray ( selection ?. points ) ? selection . points : [ ] ;
937+ const faces = Array . isArray ( selection ?. faces ) ? selection . faces : [ ] ;
938+ const basis = this . getSketchBasis ( feature ) ;
939+ if ( ! basis ) return false ;
940+ let added = 0 ;
941+ const createdIds = [ ] ;
942+ api . features . update ( feature . id , sketch => {
943+ sketch . entities = Array . isArray ( sketch . entities ) ? sketch . entities : [ ] ;
944+ const entities = sketch . entities ;
945+ const pointById = new Map ( entities . filter ( e => e ?. id ) . map ( e => [ e . id , e ] ) ) ;
946+ const ensurePoint = ( local , source = null ) => {
947+ const existing = this . findPointByCoord ( sketch , local , SKETCH_POINT_MERGE_EPS ) ;
948+ if ( existing ) {
949+ existing . derived = true ;
950+ existing . fixed = true ;
951+ if ( source ) existing . source = source ;
952+ return existing ;
953+ }
954+ const point = {
955+ id : this . newSketchEntityId ( 'point' ) ,
956+ type : 'point' ,
957+ x : local . x || 0 ,
958+ y : local . y || 0 ,
959+ fixed : true ,
960+ derived : true ,
961+ source : source || null
962+ } ;
963+ entities . push ( point ) ;
964+ pointById . set ( point . id , point ) ;
965+ added ++ ;
966+ createdIds . push ( point . id ) ;
967+ return point ;
968+ } ;
969+ const hasDerivedLine = ( source = null ) => {
970+ if ( ! source ?. a || ! source ?. b ) return null ;
971+ for ( const line of entities ) {
972+ if ( line ?. type !== 'line' || ! line ?. derived || line ?. source ?. type !== 'solid-edge' ) continue ;
973+ const ls = line . source || { } ;
974+ if ( String ( ls . solid_id || '' ) !== String ( source . solid_id || '' ) ) continue ;
975+ if ( String ( ls . solid_feature_id || '' ) !== String ( source . solid_feature_id || '' ) ) continue ;
976+ if ( ! ls ?. a || ! ls ?. b ) continue ;
977+ const sameA = Math . hypot ( ( ls . a . x || 0 ) - ( source . a . x || 0 ) , ( ls . a . y || 0 ) - ( source . a . y || 0 ) , ( ls . a . z || 0 ) - ( source . a . z || 0 ) ) < 1e-6 ;
978+ const sameB = Math . hypot ( ( ls . b . x || 0 ) - ( source . b . x || 0 ) , ( ls . b . y || 0 ) - ( source . b . y || 0 ) , ( ls . b . z || 0 ) - ( source . b . z || 0 ) ) < 1e-6 ;
979+ const swapA = Math . hypot ( ( ls . a . x || 0 ) - ( source . b . x || 0 ) , ( ls . a . y || 0 ) - ( source . b . y || 0 ) , ( ls . a . z || 0 ) - ( source . b . z || 0 ) ) < 1e-6 ;
980+ const swapB = Math . hypot ( ( ls . b . x || 0 ) - ( source . a . x || 0 ) , ( ls . b . y || 0 ) - ( source . a . y || 0 ) , ( ls . b . z || 0 ) - ( source . a . z || 0 ) ) < 1e-6 ;
981+ if ( ( sameA && sameB ) || ( swapA && swapB ) ) return line ;
982+ }
983+ return null ;
984+ } ;
985+ const ensureLine = ( aLocal , bLocal , source = null ) => {
986+ if ( ! aLocal || ! bLocal ) return null ;
987+ if ( source ) {
988+ const existing = hasDerivedLine ( source ) ;
989+ if ( existing ) return existing ;
990+ }
991+ // Endpoint points are often shared by multiple derived edges.
992+ // Keep them unsourced so refresh uses line sources only.
993+ const p1 = ensurePoint ( aLocal , null ) ;
994+ const p2 = ensurePoint ( bLocal , null ) ;
995+ if ( ! p1 || ! p2 || p1 . id === p2 . id ) return null ;
996+ const line = {
997+ id : this . newSketchEntityId ( 'line' ) ,
998+ type : 'line' ,
999+ a : p1 . id ,
1000+ b : p2 . id ,
1001+ construction : false ,
1002+ fixed : true ,
1003+ derived : true ,
1004+ source : source || null
1005+ } ;
1006+ entities . push ( line ) ;
1007+ added ++ ;
1008+ createdIds . push ( line . id ) ;
1009+ return line ;
1010+ } ;
1011+
1012+ for ( const p of points ) {
1013+ const local = p ?. local || null ;
1014+ if ( ! local ) continue ;
1015+ ensurePoint ( local , p ?. source || null ) ;
1016+ }
1017+ for ( const e of edges ) {
1018+ ensureLine ( e ?. aLocal || null , e ?. bLocal || null , e ?. source || null ) ;
1019+ }
1020+ for ( const faceKey of faces ) {
1021+ const segs = api . solids ?. getFaceBoundarySegments ?. ( faceKey ) || [ ] ;
1022+ const solidId = String ( faceKey || '' ) . split ( ':' ) . slice ( 0 , - 1 ) . join ( ':' ) ;
1023+ const faceIdRaw = String ( faceKey || '' ) . split ( ':' ) . slice ( - 1 ) [ 0 ] ;
1024+ const faceId = Number ( faceIdRaw ) ;
1025+ for ( const seg of segs ) {
1026+ if ( ! seg ?. a || ! seg ?. b ) continue ;
1027+ const aLocal = this . worldToSketchLocal ( seg . a , basis ) ;
1028+ const bLocal = this . worldToSketchLocal ( seg . b , basis ) ;
1029+ if ( ! aLocal || ! bLocal ) continue ;
1030+ ensureLine ( aLocal , bLocal , {
1031+ type : 'solid-edge' ,
1032+ solid_id : solidId ,
1033+ solid_feature_id : null ,
1034+ face_id : Number . isFinite ( faceId ) ? faceId : null ,
1035+ edge_index : null ,
1036+ a : { x : seg . a . x , y : seg . a . y , z : seg . a . z } ,
1037+ b : { x : seg . b . x , y : seg . b . y , z : seg . b . z }
1038+ } ) ;
1039+ }
1040+ }
1041+ } , {
1042+ opType : 'feature.update' ,
1043+ payload : {
1044+ field : 'entities.add' ,
1045+ entity : 'derived-batch' ,
1046+ counts : { edges : edges . length , points : points . length , faces : faces . length }
1047+ }
1048+ } ) ;
1049+ if ( ! added ) return false ;
1050+ this . selectedSketchEntities . clear ( ) ;
1051+ this . selectedSketchArcCenters ?. clear ?. ( ) ;
1052+ for ( const id of createdIds ) this . selectedSketchEntities . add ( id ) ;
1053+ return true ;
1054+ }
1055+
9331056function refreshDerivedSketchGeometry ( feature ) {
9341057 if ( ! feature || feature . type !== 'sketch' ) return false ;
9351058 const entities = Array . isArray ( feature . entities ) ? feature . entities : [ ] ;
@@ -1003,5 +1126,6 @@ export {
10031126 convertArcToCircleInSketch ,
10041127 createDerivedSketchPoint ,
10051128 createDerivedSketchLine ,
1129+ deriveSelectionsAtomic ,
10061130 refreshDerivedSketchGeometry
10071131} ;
0 commit comments