@@ -301,16 +301,110 @@ function fixElementFromRust(el: any): any {
301301 } ;
302302}
303303
304+ function normalizeViewer3DStateForRust ( vs : any ) : any {
305+ if ( ! vs || typeof vs !== "object" ) return vs ;
306+
307+ const n = ( v : any , fallback : number ) =>
308+ typeof v === "number" && Number . isFinite ( v ) ? v : fallback ;
309+
310+ const b = ( v : any , fallback : boolean ) =>
311+ typeof v === "boolean" ? v : fallback ;
312+
313+ const s = ( v : any , fallback : string ) =>
314+ typeof v === "string" ? v : fallback ;
315+
316+ const arr3 = ( v : any , fallback : [ number , number , number ] ) : [ number , number , number ] =>
317+ Array . isArray ( v ) && v . length === 3 && v . every ( ( x : any ) => typeof x === "number" )
318+ ? ( v as [ number , number , number ] )
319+ : fallback ;
320+
321+ const arr4 = ( v : any , fallback : [ number , number , number , number ] ) : [ number , number , number , number ] =>
322+ Array . isArray ( v ) && v . length === 4 && v . every ( ( x : any ) => typeof x === "number" )
323+ ? ( v as [ number , number , number , number ] )
324+ : fallback ;
325+
326+ const cam = vs . camera ?? { } ;
327+ const dsp = vs . display ?? { } ;
328+ const mat = vs . material ?? { } ;
329+ const clip = vs . clipping ?? { } ;
330+ const expl = vs . explode ?? { } ;
331+ const zeb = vs . zebra ?? { } ;
332+
333+ const normalizeClipPlane = ( cp : any ) => ( {
334+ enabled : b ( cp ?. enabled , false ) ,
335+ value : n ( cp ?. value , 0 ) ,
336+ normal : cp ?. normal != null ? arr3 ( cp . normal , [ 0 , 0 , 0 ] ) : null ,
337+ } ) ;
338+
339+ return {
340+ camera : {
341+ control : s ( cam . control , "orbit" ) ,
342+ ortho : b ( cam . ortho , true ) ,
343+ up : s ( cam . up , "Z" ) ,
344+ position : arr3 ( cam . position , [ 0 , 0 , 0 ] ) ,
345+ quaternion : arr4 ( cam . quaternion , [ 0 , 0 , 0 , 1 ] ) ,
346+ target : arr3 ( cam . target , [ 0 , 0 , 0 ] ) ,
347+ zoom : n ( cam . zoom , 1 ) ,
348+ panSpeed : n ( cam . panSpeed , 1 ) ,
349+ rotateSpeed : n ( cam . rotateSpeed , 1 ) ,
350+ zoomSpeed : n ( cam . zoomSpeed , 1 ) ,
351+ holroyd : b ( cam . holroyd , false ) ,
352+ } ,
353+ display : {
354+ wireframe : b ( dsp . wireframe , false ) ,
355+ transparent : b ( dsp . transparent , false ) ,
356+ blackEdges : b ( dsp . blackEdges , true ) ,
357+ grid : dsp . grid ?? { type : "uniform" , value : false } ,
358+ axesVisible : b ( dsp . axesVisible , false ) ,
359+ axesAtOrigin : b ( dsp . axesAtOrigin , true ) ,
360+ } ,
361+ material : {
362+ metalness : n ( mat . metalness , 0.3 ) ,
363+ roughness : n ( mat . roughness , 0.65 ) ,
364+ defaultOpacity : n ( mat . defaultOpacity , 0.5 ) ,
365+ edgeColor : n ( mat . edgeColor , 0x707070 ) ,
366+ ambientIntensity : n ( mat . ambientIntensity , 1.0 ) ,
367+ directIntensity : n ( mat . directIntensity , 1.1 ) ,
368+ } ,
369+ clipping : {
370+ x : normalizeClipPlane ( clip . x ) ,
371+ y : normalizeClipPlane ( clip . y ) ,
372+ z : normalizeClipPlane ( clip . z ) ,
373+ intersection : b ( clip . intersection , false ) ,
374+ showPlanes : b ( clip . showPlanes , false ) ,
375+ objectColorCaps : b ( clip . objectColorCaps , false ) ,
376+ } ,
377+ explode : {
378+ active : b ( expl . active , false ) ,
379+ value : n ( expl . value , 0 ) ,
380+ } ,
381+ zebra : {
382+ active : b ( zeb . active , false ) ,
383+ stripeCount : toInteger ( zeb . stripeCount , 6 ) ,
384+ stripeDirection : n ( zeb . stripeDirection , 0 ) ,
385+ colorScheme : s ( zeb . colorScheme , "blackwhite" ) ,
386+ opacity : n ( zeb . opacity , 1 ) ,
387+ mappingMode : s ( zeb . mappingMode , "reflection" ) ,
388+ } ,
389+ } ;
390+ }
391+
304392function fixElementToRust ( el : any ) : any {
305393 if ( ! el ) return el ;
306394
307395 fixStylesHatch ( el , false ) ;
308396 fixCustomDataToRust ( el ) ;
309- if ( el . type === "model" && el . viewerState ?. display ?. grid ) {
310- el . viewerState = {
311- ...el . viewerState ,
312- display : { ...el . viewerState . display , grid : fixViewer3DGridToRust ( el . viewerState . display . grid ) } ,
313- } ;
397+
398+ if ( el . type === "model" ) {
399+ if ( el . viewerState ) {
400+ el . viewerState = normalizeViewer3DStateForRust ( el . viewerState ) ;
401+ }
402+ if ( el . viewerState ?. display ?. grid ) {
403+ el . viewerState = {
404+ ...el . viewerState ,
405+ display : { ...el . viewerState . display , grid : fixViewer3DGridToRust ( el . viewerState . display . grid ) } ,
406+ } ;
407+ }
314408 }
315409
316410 // Convert TypeScript DucLine tuples [start, end] → Rust structs { start, end }
@@ -381,6 +475,10 @@ function flattenPrecisionValues(obj: any): any {
381475
382476 if ( Array . isArray ( obj ) ) return obj . map ( flattenPrecisionValues ) ;
383477
478+ if ( ArrayBuffer . isView ( obj ) || obj instanceof ArrayBuffer ) {
479+ return obj ;
480+ }
481+
384482 if ( typeof obj === "object" ) {
385483 const out : Record < string , any > = { } ;
386484 for ( const key in obj ) {
0 commit comments