sunrize 2.0.6 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +5 -5
  2. package/src/Application/Application.js +2 -9
  3. package/src/Application/Document.js +2 -0
  4. package/src/Controls/MaterialPreviewPopover.js +28 -25
  5. package/src/Controls/TexturePreviewPopover.js +14 -12
  6. package/src/Editors/Library.js +1 -1
  7. package/src/Editors/MaterialsLibrary.js +6 -50
  8. package/src/Editors/NodesLibrary.js +3 -3
  9. package/src/Editors/OutlineEditor.js +124 -29
  10. package/src/Editors/Panel.js +182 -40
  11. package/src/Editors/PrimitivesLibrary.js +2 -2
  12. package/src/Editors/ScriptEditor.js +9 -4
  13. package/src/Tools/Geometry2D/Arc2DTool.js +5 -5
  14. package/src/Tools/Geometry2D/Circle2DTool.js +5 -5
  15. package/src/Tools/Geometry2D/Polyline2DTool.js +2 -2
  16. package/src/Tools/Geometry2D/Polypoint2DTool.js +2 -2
  17. package/src/Tools/NURBS/NurbsCurveTool.js +2 -2
  18. package/src/Tools/Networking/InlineGeometryTool.js +7 -0
  19. package/src/Tools/Rendering/IndexedLineSetTool.js +2 -2
  20. package/src/Tools/Rendering/LineSetTool.js +2 -2
  21. package/src/Tools/Rendering/PointSetTool.js +2 -2
  22. package/src/Tools/Rendering/X3DGeometryNodeTool.js +39 -15
  23. package/src/Tools/Rendering/X3DGeometryNodeTool.x3d +43 -8
  24. package/src/Undo/Editor.js +52 -0
  25. package/src/assets/X3D/MaterialPreview.x3d +11 -6
  26. package/src/Tools/Rendering/X3DLineGeometryNodeTool.js +0 -40
  27. package/src/Tools/Rendering/X3DLineGeometryNodeTool.x3d +0 -64
  28. package/src/Tools/Rendering/X3DPointGeometryNodeTool.js +0 -40
  29. package/src/Tools/Rendering/X3DPointGeometryNodeTool.x3d +0 -54
  30. /package/src/{Editors → assets/X3D}/CannonExterior.avif +0 -0
  31. /package/src/{Editors → assets/X3D}/Materials.x3d +0 -0
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "sunrize",
3
3
  "productName": "Sunrize X3D Editor",
4
- "version": "2.0.6",
5
- "description": "A Multi-Platform X3D Editor",
4
+ "version": "2.0.8",
5
+ "description": "Sunrize — A Multi-Platform X3D Editor",
6
6
  "main": "src/main.js",
7
7
  "bin": {
8
8
  "sunrize": "bin/sunrize.js"
@@ -92,7 +92,7 @@
92
92
  "@vscode/codicons": "^0.0.44",
93
93
  "capitalize": "^2.0.4",
94
94
  "console": "^0.7.2",
95
- "electron": "^40.6.1",
95
+ "electron": "^41.0.0",
96
96
  "electron-prompt": "^1.7.0",
97
97
  "electron-squirrel-startup": "^1.0.1",
98
98
  "electron-tabs": "^1.0.4",
@@ -109,9 +109,9 @@
109
109
  "qtip2": "^3.0.3",
110
110
  "spectrum-colorpicker2": "^2.0.10",
111
111
  "string-similarity": "^4.0.4",
112
- "tweakpane": "^3.1.10",
112
+ "tweakpane": "^4.0.5",
113
113
  "update-electron-app": "^3.1.2",
114
- "x_ite": "^14.0.6",
114
+ "x_ite": "^14.0.9",
115
115
  "x3d-traverse": "^1.0.22"
116
116
  }
117
117
  }
@@ -51,6 +51,8 @@ module .exports = class Application
51
51
 
52
52
  constructor ()
53
53
  {
54
+ // process .env .SUNRISE_ENVIRONMENT = "DEVELOPMENT"; // Windows test
55
+
54
56
  if (process .env .SUNRISE_ENVIRONMENT === "DEVELOPMENT")
55
57
  process .env .ELECTRON_ENABLE_LOGGING = 1;
56
58
 
@@ -527,15 +529,6 @@ module .exports = class Application
527
529
  label: _("Show All Objects"),
528
530
  click: () => this .mainWindow .webContents .send ("show-all-objects"),
529
531
  },
530
- { type: "separator" },
531
- {
532
- label: _("Transform to Zero"),
533
- click: () => this .mainWindow .webContents .send ("transform-to-zero"),
534
- },
535
- {
536
- label: _("Remove Empty Groups"),
537
- click: () => this .mainWindow .webContents .send ("remove-empty-groups"),
538
- },
539
532
  ],
540
533
  },
541
534
  {
@@ -1267,6 +1267,8 @@ Viewpoint {
1267
1267
  label: _("Viewpoints"),
1268
1268
  submenu: viewpoints .filter ((_, index) => index > 0) .map ((viewpointNode, index) => ({
1269
1269
  label: `${viewpointNode ._description .getValue () || viewpointNode .getDisplayName () || `VP${index + 1}}`}`,
1270
+ type: "radio",
1271
+ checked: viewpointNode ._isBound .getValue (),
1270
1272
  args: ["bindViewpoint", index + 1],
1271
1273
  })),
1272
1274
  },
@@ -37,26 +37,22 @@ $.fn.materialPreviewPopover = async function (node)
37
37
  // Create material node.
38
38
 
39
39
  const
40
- appearanceNode = browser .currentScene .getExportedNode ("Appearance"),
41
- previewNode = node .copy (scene);
40
+ x3dSyntax = await Editor .exportX3D (node .getExecutionContext (), [node]),
41
+ nodes = await Editor .importX3D (scene, x3dSyntax),
42
+ previewNode = nodes [0],
43
+ appearanceNode = browser .currentScene .getExportedNode ("Appearance");
42
44
 
43
45
  // Assign material node.
44
46
 
45
47
  for (const field of previewNode .getFields ())
48
+ connect (field, node .getField (field .getName ()));
49
+
50
+ for (const [i, extension] of previewNode ._extensions ?.entries () ?? [ ])
46
51
  {
47
- switch (field .getType ())
48
- {
49
- case X3D .X3DConstants .SFNode:
50
- field .setValue (null);
51
- break
52
- case X3D .X3DConstants .MFNode:
53
- field .length = 0;
54
- break;
55
- default:
56
- field .addReference (node .getField (field .getName ()));
57
- field .removeFieldInterest (node .getField (field .getName ()));
58
- break;
59
- }
52
+ const original = node ._extensions [i] .getValue ();
53
+
54
+ for (const field of extension .getValue () .getFields ())
55
+ connect (field, original .getField (field .getName ()));
60
56
  }
61
57
 
62
58
  previewNode .setup ();
@@ -90,16 +86,7 @@ $.fn.materialPreviewPopover = async function (node)
90
86
  field = backPreviewNode .getField (name),
91
87
  back = `back${name [0] .toUpperCase ()}${name .slice (1)}`;
92
88
 
93
- switch (field .getType ())
94
- {
95
- case X3D .X3DConstants .SFNode:
96
- case X3D .X3DConstants .MFNode:
97
- break;
98
- default:
99
- field .addReference (node .getField (back));
100
- field .removeFieldInterest (node .getField (back));
101
- break;
102
- }
89
+ connect (field, node .getField (back));
103
90
  }
104
91
 
105
92
  appearanceNode .material = backPreviewNode;
@@ -119,6 +106,9 @@ $.fn.materialPreviewPopover = async function (node)
119
106
  events: {
120
107
  hide: (event, api) =>
121
108
  {
109
+ for (const extension of Array .from (previewNode ._extensions ?? [ ]))
110
+ extension .dispose ();
111
+
122
112
  previewNode .dispose ();
123
113
  backPreviewNode ?.dispose ();
124
114
  browser .dispose ();
@@ -135,3 +125,16 @@ $.fn.materialPreviewPopover = async function (node)
135
125
  return this;
136
126
  };
137
127
 
128
+ function connect (field, original)
129
+ {
130
+ switch (field .getType ())
131
+ {
132
+ case X3D .X3DConstants .SFNode:
133
+ case X3D .X3DConstants .MFNode:
134
+ break;
135
+ default:
136
+ original .addFieldInterest (field);
137
+ field .assign (original);
138
+ break;
139
+ }
140
+ }
@@ -69,18 +69,7 @@ $.fn.texturePreviewPopover = async function (node)
69
69
  // Assign texture node.
70
70
 
71
71
  for (const field of previewNode .getFields ())
72
- {
73
- switch (field .getType ())
74
- {
75
- case X3D .X3DConstants .SFNode:
76
- case X3D .X3DConstants .MFNode:
77
- break;
78
- default:
79
- field .addReference (node .getField (field .getName ()));
80
- field .removeFieldInterest (node .getField (field .getName ()));
81
- break;
82
- }
83
- }
72
+ connect (field, node .getField (field .getName ()));
84
73
 
85
74
  appearanceNode .texture = previewNode;
86
75
 
@@ -194,3 +183,16 @@ $.fn.texturePreviewPopover = async function (node)
194
183
  return this;
195
184
  };
196
185
 
186
+ function connect (field, original)
187
+ {
188
+ switch (field .getType ())
189
+ {
190
+ case X3D .X3DConstants .SFNode:
191
+ case X3D .X3DConstants .MFNode:
192
+ break;
193
+ default:
194
+ original .addFieldInterest (field);
195
+ field .assign (original);
196
+ break;
197
+ }
198
+ }
@@ -188,7 +188,7 @@ module .exports = new class Library extends Dialog
188
188
  const node = nodes .find (node => node .text () .toLowerCase () === input)
189
189
  ?? nodes .sort ((a, b) => a .data ("similarity") - b .data ("similarity")) .at (-1);
190
190
 
191
- node .trigger ("click");
191
+ node .trigger ("dblclick");
192
192
  }
193
193
 
194
194
  update ()
@@ -4,9 +4,10 @@ const
4
4
  $ = require ("jquery"),
5
5
  X3D = require ("../X3D"),
6
6
  LibraryPane = require ("./LibraryPane"),
7
+ Editor = require("../Undo/Editor"),
7
8
  _ = require ("../Application/GetText");
8
9
 
9
- module .exports = class Materials extends LibraryPane
10
+ module .exports = class MaterialsLibrary extends LibraryPane
10
11
  {
11
12
  id = "MATERIALS";
12
13
  description = "Materials";
@@ -36,7 +37,7 @@ module .exports = class Materials extends LibraryPane
36
37
  const
37
38
  canvas = $("<x3d-canvas preserveDrawingBuffer='true' xrSessionMode='NONE'></x3d-canvas>"),
38
39
  browser = canvas .prop ("browser"),
39
- scene = await browser .createX3DFromURL (new X3D .MFString (`file://${__dirname}/Materials.x3d`));
40
+ scene = await browser .createX3DFromURL (new X3D .MFString (`file://${__dirname}/../assets/X3D/Materials.x3d`));
40
41
 
41
42
  // Buttons
42
43
 
@@ -83,7 +84,7 @@ module .exports = class Materials extends LibraryPane
83
84
  .addClass ("text")
84
85
  .text (`${group .getNodeName ()} ${c + 1}`))
85
86
  .appendTo (this .#list)
86
- .on ("click", () => this .importMaterial (material .getNodeName ())));
87
+ .on ("dblclick", () => this .importMaterial (material .getNodeName ())));
87
88
  }
88
89
  }
89
90
 
@@ -113,7 +114,7 @@ module .exports = class Materials extends LibraryPane
113
114
  const
114
115
  canvas = $("<x3d-canvas preserveDrawingBuffer='true' xrSessionMode='NONE'></x3d-canvas>"),
115
116
  browser = canvas .prop ("browser"),
116
- scene = await browser .createX3DFromURL (new X3D .MFString (`file://${__dirname}/Materials.x3d`));
117
+ scene = await browser .createX3DFromURL (new X3D .MFString (`file://${__dirname}/../assets/X3D/Materials.x3d`));
117
118
 
118
119
  scene .addComponent (browser .getComponent ("X_ITE"));
119
120
 
@@ -146,7 +147,7 @@ module .exports = class Materials extends LibraryPane
146
147
 
147
148
  if (this .config .global .convertToPhysical)
148
149
  {
149
- const material = this .convertPhongToPhysical (scene, appearance .material);
150
+ const material = Editor .convertPhongToPhysical (scene, appearance .material, null);
150
151
 
151
152
  scene .updateNamedNode (appearance .material .getNodeName (), material);
152
153
 
@@ -170,49 +171,4 @@ module .exports = class Materials extends LibraryPane
170
171
  browser .dispose ();
171
172
  canvas .remove ();
172
173
  }
173
-
174
- convertPhongToPhysical (executionContext, phong)
175
- {
176
- let
177
- physical = executionContext .createNode ("PhysicalMaterial"),
178
- baseColor = phong .diffuseColor .sRGBToLinear (),
179
- specularColor = phong .specularColor .sRGBToLinear (),
180
- specularIntensity = Math .max (... specularColor),
181
- metallic = Math .min (Math .max ((specularIntensity - 0.04) / (1.0 - 0.04), 0), 1) * 0.5,
182
- roughness = 1 - phong .shininess,
183
- emissiveColor = phong .emissiveColor .sRGBToLinear (),
184
- transparency = phong .transparency,
185
- transmission = transparency ** (1/3);
186
-
187
- if ([... specularColor] .some (Boolean) && roughness)
188
- {
189
- const specularMaterial = executionContext .createNode ("SpecularMaterialExtension");
190
-
191
- specularMaterial .specularColor = specularColor;
192
- specularMaterial .specularStrength = 10 * roughness;
193
-
194
- physical .extensions .push (specularMaterial);
195
-
196
- metallic *= 0.1;
197
- roughness *= 0.5;
198
- }
199
-
200
- if (transparency)
201
- {
202
- const transmissionMaterial = executionContext .createNode ("TransmissionMaterialExtension");
203
-
204
- transmissionMaterial .transmission = transmission;
205
-
206
- physical .extensions .push (transmissionMaterial);
207
-
208
- roughness *= 0.5 * (1 - transparency);
209
- }
210
-
211
- physical .baseColor = baseColor;
212
- physical .metallic = metallic;
213
- physical .roughness = roughness;
214
- physical .emissiveColor = emissiveColor;
215
-
216
- return physical;
217
- }
218
174
  };
@@ -77,7 +77,7 @@ module .exports = class NodesLibrary extends LibraryPane
77
77
  .text (node .typeName)
78
78
  .attr ("componentName", node .componentInfo .name)
79
79
  .appendTo (this .#list)
80
- .on ("click", () => this .createNode (node .typeName, node .componentInfo .name));
80
+ .on ("dblclick", () => this .createNode (node .typeName, node .componentInfo .name));
81
81
  }
82
82
  }
83
83
 
@@ -97,7 +97,7 @@ module .exports = class NodesLibrary extends LibraryPane
97
97
  .addClass ("node")
98
98
  .text (proto .name)
99
99
  .appendTo (this .#list)
100
- .on ("click", () => this .createProto (proto));
100
+ .on ("dblclick", () => this .createProto (proto));
101
101
  }
102
102
  }
103
103
 
@@ -123,7 +123,7 @@ module .exports = class NodesLibrary extends LibraryPane
123
123
  .text (node .typeName)
124
124
  .attr ("componentName", node .componentInfo .name)
125
125
  .appendTo (this .#list)
126
- .on ("click", () => this .createNode (node .typeName, node .componentInfo .name));
126
+ .on ("dblclick", () => this .createNode (node .typeName, node .componentInfo .name));
127
127
  }
128
128
  }
129
129
 
@@ -24,9 +24,6 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
24
24
 
25
25
  electron .ipcRenderer .on ("outline-editor", (event, key, ... args) => this [key] (... args));
26
26
 
27
- electron .ipcRenderer .on ("transform-to-zero", () => this .transformToZero ());
28
- electron .ipcRenderer .on ("remove-empty-groups", () => this .removeEmptyGroups ());
29
-
30
27
  this .setup ();
31
28
  }
32
29
 
@@ -54,26 +51,6 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
54
51
  ]);
55
52
  }
56
53
 
57
- transformToZero ()
58
- {
59
- const
60
- selection = this .sceneGraph .find (".node.primary, .node.manually"),
61
- ids = selection .map (function () { return this .id }) .get (),
62
- nodes = ids .length ? ids .map (id => this .getNode ($(`#${id}`))) : this .executionContext .rootNodes;
63
-
64
- Editor .transformToZero (this .executionContext, nodes);
65
- }
66
-
67
- removeEmptyGroups ()
68
- {
69
- const
70
- selection = this .sceneGraph .find (".node.primary, .node.manually"),
71
- ids = selection .map (function () { return this .id }) .get (),
72
- nodes = ids .length ? ids .map (id => this .getNode ($(`#${id}`))) : this .executionContext .rootNodes;
73
-
74
- Editor .removeEmptyGroups (this .executionContext, nodes);
75
- }
76
-
77
54
  showContextMenu (event)
78
55
  {
79
56
  const element = $(document .elementFromPoint (event .pageX, event .pageY))
@@ -305,6 +282,14 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
305
282
  });
306
283
  }
307
284
 
285
+ if (!node .getType () .includes (X3D .X3DConstants .InlineGeometry))
286
+ {
287
+ menu .push ({
288
+ label: _("Convert Node to InlineGeometry..."),
289
+ args: ["convertNodeToInlineFile", element .attr ("id"), executionContext .getId (), node .getId (), "InlineGeometry"],
290
+ });
291
+ }
292
+
308
293
  continue;
309
294
  }
310
295
  case X3D .X3DConstants .ImageTexture:
@@ -334,6 +319,21 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
334
319
 
335
320
  continue;
336
321
  }
322
+ case X3D .X3DConstants .InlineGeometry:
323
+ {
324
+ menu .push ({
325
+ label: _("Open InlineGeometry Scene in New Tab"),
326
+ enabled: node .checkLoadState () === X3D .X3DConstants .COMPLETE_STATE,
327
+ args: ["openFileInNewTab", node .getInternalScene () ?.worldURL],
328
+ },
329
+ {
330
+ label: _("Fold InlineGeometry Back into Scene"),
331
+ enabled: node .checkLoadState () === X3D .X3DConstants .COMPLETE_STATE && !!node .getGeometry (),
332
+ args: ["foldInlineGeometryBackIntoScene", element .attr ("id"), executionContext .getId (), node .getId ()],
333
+ });
334
+
335
+ continue;
336
+ }
337
337
  case X3D .X3DConstants .PixelTexture:
338
338
  {
339
339
  menu .push ({
@@ -360,6 +360,15 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
360
360
 
361
361
  continue;
362
362
  }
363
+ case X3D .X3DConstants .Material:
364
+ {
365
+ menu .push ({
366
+ label: _("Convert Node to PhysicalMaterial"),
367
+ args: ["convertToPhysicalMaterial", element .attr ("id"), executionContext .getId (), node .getId ()],
368
+ });
369
+
370
+ continue;
371
+ }
363
372
  case X3D .X3DConstants .X3DPrototypeInstance:
364
373
  {
365
374
  if (!$.try (() => node .getInnerNode ()))
@@ -379,7 +388,7 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
379
388
  {
380
389
  menu .push ({
381
390
  label: _("Convert Node to Inline File..."),
382
- args: ["convertNodeToInlineFile", element .attr ("id"), executionContext .getId (), node .getId ()],
391
+ args: ["convertNodeToInlineFile", element .attr ("id"), executionContext .getId (), node .getId (), "Inline"],
383
392
  });
384
393
 
385
394
  continue;
@@ -401,6 +410,15 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
401
410
 
402
411
  continue;
403
412
  }
413
+ case X3D .X3DConstants .X3DTransformNode:
414
+ {
415
+ menu .push ({
416
+ label: _("Transform to Zero"),
417
+ args: ["transformToZero", element .attr ("id"), executionContext .getId (), node .getId ()],
418
+ });
419
+
420
+ continue;
421
+ }
404
422
  case X3D .X3DConstants .X3DUrlObject:
405
423
  {
406
424
  if (node ._url .some (fileURL => !fileURL .match (/^\s*(?:data|ecmascript|javascript|vrmlscript):/s)))
@@ -1321,6 +1339,25 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1321
1339
  UndoManager .shared .endUndo ();
1322
1340
  }
1323
1341
 
1342
+ transformToZero (id, executionContextId, nodeId)
1343
+ {
1344
+ const
1345
+ executionContext = this .objects .get (executionContextId),
1346
+ transformNode = this .objects .get (nodeId);
1347
+
1348
+ Editor .transformToZero (executionContext, [transformNode]);
1349
+ }
1350
+
1351
+ removeEmptyGroups ()
1352
+ {
1353
+ const
1354
+ selection = this .sceneGraph .find (".node.primary, .node.manually"),
1355
+ ids = selection .map (function () { return this .id }) .get (),
1356
+ nodes = ids .length ? ids .map (id => this .getNode ($(`#${id}`))) : this .executionContext .rootNodes;
1357
+
1358
+ Editor .removeEmptyGroups (this .executionContext, nodes);
1359
+ }
1360
+
1324
1361
  convertImageTextureToPixelTexture (id, executionContextId, nodeId)
1325
1362
  {
1326
1363
  const
@@ -1438,6 +1475,26 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1438
1475
  UndoManager .shared .endUndo ();
1439
1476
  }
1440
1477
 
1478
+ convertToPhysicalMaterial (id, executionContextId, nodeId)
1479
+ {
1480
+ const
1481
+ executionContext = this .objects .get (executionContextId),
1482
+ materialNode = this .objects .get (nodeId);
1483
+
1484
+ // Add undo step.
1485
+
1486
+ UndoManager .shared .beginUndo (_("Convert Node to PhysicalMaterial"));
1487
+
1488
+ const physicalMaterialNode = Editor .convertPhongToPhysical (executionContext, new X3D .SFNode (materialNode))
1489
+
1490
+ if (materialNode .getName ())
1491
+ Editor .updateNamedNode (executionContext, materialNode .getName (), physicalMaterialNode);
1492
+
1493
+ Editor .replaceAllOccurrences (executionContext, materialNode, physicalMaterialNode);
1494
+
1495
+ UndoManager .shared .endUndo ();
1496
+ }
1497
+
1441
1498
  async convertPixelTextureToImageTexture (id, executionContextId, nodeId)
1442
1499
  {
1443
1500
  const
@@ -1511,8 +1568,8 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1511
1568
 
1512
1569
  const
1513
1570
  rootNodes = executionContext .rootNodes .copy (),
1514
- nodesToImport = [... inlineNode .getInternalScene () .rootNodes] .map (node => node .getValue ()),
1515
- x3dSyntax = await Editor .exportX3D (inlineNode .getInternalScene (), nodesToImport, { importedNodes: true }),
1571
+ nodesToExport = [... inlineNode .getInternalScene () .rootNodes] .map (node => node .getValue ()),
1572
+ x3dSyntax = await Editor .exportX3D (inlineNode .getInternalScene (), nodesToExport, { importedNodes: true }),
1516
1573
  nodes = await Editor .importX3D (executionContext, x3dSyntax);
1517
1574
 
1518
1575
  // Remove imported nodes from root nodes.
@@ -1557,6 +1614,36 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1557
1614
  this .expandTo (childNode, { expandObject: true });
1558
1615
  }
1559
1616
 
1617
+ async foldInlineGeometryBackIntoScene (id, executionContextId, nodeId)
1618
+ {
1619
+ const
1620
+ executionContext = this .objects .get (executionContextId),
1621
+ inlineGeometryNode = this .objects .get (nodeId);
1622
+
1623
+ UndoManager .shared .beginUndo (_("Fold InlineGeometry Back into Scene"));
1624
+
1625
+ const
1626
+ rootNodes = executionContext .rootNodes .copy (),
1627
+ nodesToExport = [inlineGeometryNode .getGeometry ()],
1628
+ x3dSyntax = await Editor .exportX3D (inlineGeometryNode .getInternalScene (), nodesToExport),
1629
+ nodes = await Editor .importX3D (executionContext, x3dSyntax),
1630
+ geometryNode = nodes [0];
1631
+
1632
+ // Remove imported nodes from root nodes.
1633
+
1634
+ Editor .setFieldValue (executionContext, executionContext, executionContext .rootNodes, rootNodes);
1635
+
1636
+ // Insert InlineGeometry node.
1637
+
1638
+ Editor .replaceAllOccurrences (executionContext, inlineGeometryNode, geometryNode);
1639
+
1640
+ UndoManager .shared .endUndo ();
1641
+
1642
+ await this .browser .nextFrame ();
1643
+
1644
+ this .expandTo (geometryNode, { expandObject: true });
1645
+ }
1646
+
1560
1647
  protocolToMimeType = new Map ([
1561
1648
  ["ecmascript:", "application/ecmascript"],
1562
1649
  ["javascript:", "application/javascript"],
@@ -1707,7 +1794,7 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1707
1794
  UndoManager .shared .endUndo ();
1708
1795
  }
1709
1796
 
1710
- async convertNodeToInlineFile (id, executionContextId, nodeId)
1797
+ async convertNodeToInlineFile (id, executionContextId, nodeId, typeName)
1711
1798
  {
1712
1799
  const
1713
1800
  executionContext = this .objects .get (executionContextId),
@@ -1719,7 +1806,15 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1719
1806
  if (response .canceled)
1720
1807
  return;
1721
1808
 
1722
- UndoManager .shared .beginUndo (_("Convert Node to Inline File"));
1809
+ switch (typeName)
1810
+ {
1811
+ case "Inline":
1812
+ UndoManager .shared .beginUndo (_("Convert Node to Inline File"));
1813
+ break;
1814
+ case "InlineGeometry":
1815
+ UndoManager .shared .beginUndo (_("Convert Node to InlineGeometry"));
1816
+ break;
1817
+ }
1723
1818
 
1724
1819
  // Create inline file.
1725
1820
 
@@ -1729,7 +1824,7 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1729
1824
 
1730
1825
  await Editor .addComponent (executionContext, "Networking");
1731
1826
 
1732
- const inlineNode = executionContext .createNode ("Inline") .getValue ();
1827
+ const inlineNode = executionContext .createNode (typeName) .getValue ();
1733
1828
 
1734
1829
  inlineNode ._url = [Editor .relativePath (executionContext, response .filePath)];
1735
1830