This commit is contained in:
kovacsv 2022-04-18 18:09:17 +02:00
parent 4f627a7589
commit 267aeec79a
11 changed files with 230 additions and 28 deletions

View File

@ -1,4 +1,5 @@
import { Exporter3dm } from './exporter3dm.js'; import { Exporter3dm } from './exporter3dm.js';
import { ExporterBim } from './exporterbim.js';
import { ExporterGltf } from './exportergltf.js'; import { ExporterGltf } from './exportergltf.js';
import { ExporterModel } from './exportermodel.js'; import { ExporterModel } from './exportermodel.js';
import { ExporterObj } from './exporterobj.js'; import { ExporterObj } from './exporterobj.js';
@ -16,7 +17,8 @@ export class Exporter
new ExporterPly (), new ExporterPly (),
new ExporterOff (), new ExporterOff (),
new ExporterGltf (), new ExporterGltf (),
new Exporter3dm () new Exporter3dm (),
new ExporterBim ()
]; ];
} }

View File

@ -0,0 +1,77 @@
import { FileFormat } from '../io/fileutils.js';
import { ColorComponentFromFloat } from '../model/color.js';
import { ConvertMeshToMeshBuffer } from '../model/meshbuffer.js';
import { PropertyToString } from '../model/property.js';
import { ExportedFile, ExporterBase } from './exporterbase.js';
export class ExporterBim extends ExporterBase
{
constructor ()
{
super ();
}
CanExport (format, extension)
{
return format === FileFormat.Text && extension === 'bim';
}
ExportContent (exporterModel, format, files, onFinish)
{
let bimContent = {
schema_version : '1.0.0',
meshes : [],
elements : []
};
this.ExportProperties (exporterModel.GetModel (), bimContent);
let meshId = 0;
exporterModel.EnumerateTransformedMeshes ((mesh) => {
let meshBuffer = ConvertMeshToMeshBuffer (mesh);
for (let primitiveIndex = 0; primitiveIndex < meshBuffer.PrimitiveCount (); primitiveIndex++) {
let primitive = meshBuffer.GetPrimitive (primitiveIndex);
let material = exporterModel.GetMaterial (primitive.material);
let bimMesh = {
mesh_id : meshId,
coordinates : primitive.vertices,
indices : primitive.indices
};
let bimElement = {
mesh_id : meshId,
type : 'Other',
color : {
r : material.color.r,
g : material.color.g,
b : material.color.b,
a : ColorComponentFromFloat (material.opacity)
}
};
this.ExportProperties (mesh, bimElement);
bimContent.meshes.push (bimMesh);
bimContent.elements.push (bimElement);
meshId += 1;
}
});
let bimFile = new ExportedFile ('model.bim');
bimFile.SetTextContent (JSON.stringify (bimContent, null, 4));
files.push (bimFile);
onFinish ();
}
ExportProperties (element, targetObject)
{
let info = {};
for (let groupIndex = 0; groupIndex < element.PropertyGroupCount (); groupIndex++) {
let group = element.GetPropertyGroup (groupIndex);
for (let propertyIndex = 0; propertyIndex < group.PropertyCount (); propertyIndex++) {
let property = group.GetProperty (propertyIndex);
info[property.name] = PropertyToString (property);
}
}
if (Object.keys (info).length !== 0) {
targetObject.info = info;
}
}
}

View File

@ -23,6 +23,11 @@ export class ExporterModel
this.settings = settings || new ExporterSettings (); this.settings = settings || new ExporterSettings ();
} }
GetModel ()
{
return this.model;
}
MaterialCount () MaterialCount ()
{ {
return this.model.MaterialCount (); return this.model.MaterialCount ();

View File

@ -3,6 +3,7 @@ import { TaskRunner, RunTaskAsync, RunTasks, RunTasksBatch, WaitWhile } from './
import { Exporter } from './export/exporter.js'; import { Exporter } from './export/exporter.js';
import { Exporter3dm } from './export/exporter3dm.js'; import { Exporter3dm } from './export/exporter3dm.js';
import { ExportedFile, ExporterBase } from './export/exporterbase.js'; import { ExportedFile, ExporterBase } from './export/exporterbase.js';
import { ExporterBim } from './export/exporterbim.js';
import { ExporterGltf } from './export/exportergltf.js'; import { ExporterGltf } from './export/exportergltf.js';
import { ExporterSettings, ExporterModel } from './export/exportermodel.js'; import { ExporterSettings, ExporterModel } from './export/exportermodel.js';
import { ExporterObj } from './export/exporterobj.js'; import { ExporterObj } from './export/exporterobj.js';
@ -54,7 +55,7 @@ import { FinalizeModel, CheckModel } from './model/modelfinalization.js';
import { IsModelEmpty, GetBoundingBox, GetTopology, IsSolid, HasDefaultMaterial, ReplaceDefaultMaterialColor } from './model/modelutils.js'; import { IsModelEmpty, GetBoundingBox, GetTopology, IsSolid, HasDefaultMaterial, ReplaceDefaultMaterialColor } from './model/modelutils.js';
import { Node, NodeType } from './model/node.js'; import { Node, NodeType } from './model/node.js';
import { Object3D, ModelObject3D } from './model/object.js'; import { Object3D, ModelObject3D } from './model/object.js';
import { Property, PropertyGroup, PropertyType } from './model/property.js'; import { Property, PropertyGroup, PropertyToString, PropertyType } from './model/property.js';
import { GetTriangleArea, GetTetrahedronSignedVolume, CalculateVolume, CalculateSurfaceArea } from './model/quantities.js'; import { GetTriangleArea, GetTetrahedronSignedVolume, CalculateVolume, CalculateSurfaceArea } from './model/quantities.js';
import { TopologyVertex, TopologyEdge, TopologyTriangleEdge, TopologyTriangle, Topology } from './model/topology.js'; import { TopologyVertex, TopologyEdge, TopologyTriangleEdge, TopologyTriangle, Topology } from './model/topology.js';
import { Triangle } from './model/triangle.js'; import { Triangle } from './model/triangle.js';
@ -82,6 +83,7 @@ export {
Exporter3dm, Exporter3dm,
ExportedFile, ExportedFile,
ExporterBase, ExporterBase,
ExporterBim,
ExporterGltf, ExporterGltf,
ExporterSettings, ExporterSettings,
ExporterModel, ExporterModel,
@ -244,6 +246,7 @@ export {
ModelObject3D, ModelObject3D,
Property, Property,
PropertyGroup, PropertyGroup,
PropertyToString,
PropertyType, PropertyType,
GetTriangleArea, GetTriangleArea,
GetTetrahedronSignedVolume, GetTetrahedronSignedVolume,

View File

@ -141,6 +141,7 @@ export class Mesh extends ModelObject3D
let cloned = new Mesh (); let cloned = new Mesh ();
cloned.SetName (this.GetName ()); cloned.SetName (this.GetName ());
this.CloneProperties (cloned);
for (let i = 0; i < this.VertexCount (); i++) { for (let i = 0; i < this.VertexCount (); i++) {
let vertex = this.GetVertex (i); let vertex = this.GetVertex (i);

View File

@ -80,4 +80,11 @@ export class ModelObject3D extends Object3D
{ {
return this.propertyGroups[index]; return this.propertyGroups[index];
} }
CloneProperties (target)
{
for (let propertyGroup of this.propertyGroups) {
target.AddPropertyGroup (propertyGroup.Clone ());
}
}
} }

View File

@ -1,3 +1,5 @@
import { ColorToHexString } from './color.js';
export const PropertyType = export const PropertyType =
{ {
Text : 1, Text : 1,
@ -16,6 +18,16 @@ export class Property
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
Clone ()
{
const clonable = (this.type === PropertyType.Color);
if (clonable) {
return new Property (this.type, this.name, this.value.Clone ());
} else {
return new Property (this.type, this.name, this.value);
}
}
} }
export class PropertyGroup export class PropertyGroup
@ -40,4 +52,34 @@ export class PropertyGroup
{ {
return this.properties[index]; return this.properties[index];
} }
Clone ()
{
let cloned = new PropertyGroup (this.name);
for (let property of this.properties) {
cloned.AddProperty (property.Clone ());
}
return cloned;
}
}
export function PropertyToString (property)
{
if (property.type === PropertyType.Text) {
return property.value;
} else if (property.type === PropertyType.Integer) {
return property.value.toLocaleString ();
} else if (property.type === PropertyType.Number) {
return property.value.toLocaleString (undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
} else if (property.type === PropertyType.Boolean) {
return property.value ? 'True' : 'False';
} else if (property.type === PropertyType.Percent) {
return parseInt (property.value * 100, 10).toString () + '%';
} else if (property.type === PropertyType.Color) {
return '#' + ColorToHexString (property.value);
}
return null;
} }

View File

@ -2,7 +2,7 @@ import { RunTaskAsync } from '../engine/core/taskrunner.js';
import { SubCoord3D } from '../engine/geometry/coord3d.js'; import { SubCoord3D } from '../engine/geometry/coord3d.js';
import { GetBoundingBox, IsSolid } from '../engine/model/modelutils.js'; import { GetBoundingBox, IsSolid } from '../engine/model/modelutils.js';
import { CalculateVolume, CalculateSurfaceArea } from '../engine/model/quantities.js'; import { CalculateVolume, CalculateSurfaceArea } from '../engine/model/quantities.js';
import { Property, PropertyType } from '../engine/model/property.js'; import { Property, PropertyToString, PropertyType } from '../engine/model/property.js';
import { AddDiv, AddDomElement, ClearDomElement } from '../engine/viewer/domutils.js'; import { AddDiv, AddDomElement, ClearDomElement } from '../engine/viewer/domutils.js';
import { SidebarPanel } from './sidebarpanel.js'; import { SidebarPanel } from './sidebarpanel.js';
import { CreateInlineColorCircle } from './utils.js'; import { CreateInlineColorCircle } from './utils.js';
@ -164,24 +164,15 @@ export class SidebarDetailsPanel extends SidebarPanel
valueHtml = '<a target="_blank" href="' + property.value + '">' + property.value + '</a>'; valueHtml = '<a target="_blank" href="' + property.value + '">' + property.value + '</a>';
valueTitle = property.value; valueTitle = property.value;
} else { } else {
valueHtml = property.value; valueHtml = PropertyToString (property);
} }
} else if (property.type === PropertyType.Integer) {
valueHtml = property.value.toLocaleString ();
} else if (property.type === PropertyType.Number) {
valueHtml = property.value.toLocaleString (undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
} else if (property.type === PropertyType.Boolean) {
valueHtml = property.value ? 'True' : 'False';
} else if (property.type === PropertyType.Percent) {
valueHtml = parseInt (property.value * 100, 10).toString () + '%';
} else if (property.type === PropertyType.Color) { } else if (property.type === PropertyType.Color) {
let hexString = '#' + ColorToHexString (property.value); let hexString = '#' + ColorToHexString (property.value);
let colorCircle = CreateInlineColorCircle (property.value); let colorCircle = CreateInlineColorCircle (property.value);
targetDiv.appendChild (colorCircle); targetDiv.appendChild (colorCircle);
AddDomElement (targetDiv, 'span', null, hexString); AddDomElement (targetDiv, 'span', null, hexString);
} else {
valueHtml = PropertyToString (property);
} }
if (valueHtml !== null) { if (valueHtml !== null) {
targetDiv.innerHTML = valueHtml; targetDiv.innerHTML = valueHtml;

View File

@ -133,16 +133,20 @@ function ExportImport (model, format, extension, onReady)
}); });
} }
function CheckModelBounds (model, model2)
{
let modelBounds = OV.GetBoundingBox (model);
let model2Bounds = OV.GetBoundingBox (model2);
assert.ok (OV.CoordIsEqual3D (modelBounds.min, model2Bounds.min));
assert.ok (OV.CoordIsEqual3D (modelBounds.max, model2Bounds.max));
}
function CheckSingleMeshModel (model, model2) function CheckSingleMeshModel (model, model2)
{ {
assert.strictEqual (model2.MaterialCount (), 1); assert.strictEqual (model2.MaterialCount (), 1);
assert.strictEqual (model2.MeshInstanceCount (), 1); assert.strictEqual (model2.MeshInstanceCount (), 1);
assert.strictEqual (model.TriangleCount (), model2.TriangleCount ()); assert.strictEqual (model.TriangleCount (), model2.TriangleCount ());
CheckModelBounds (model, model2);
let modelBounds = OV.GetBoundingBox (model);
let model2Bounds = OV.GetBoundingBox (model2);
assert.ok (OV.CoordIsEqual3D (modelBounds.min, model2Bounds.min));
assert.ok (OV.CoordIsEqual3D (modelBounds.max, model2Bounds.max));
} }
function CheckModel (model, model2) function CheckModel (model, model2)
@ -150,11 +154,7 @@ function CheckModel (model, model2)
assert.strictEqual (model.MaterialCount (), model2.MaterialCount ()); assert.strictEqual (model.MaterialCount (), model2.MaterialCount ());
assert.strictEqual (model.MeshInstanceCount (), model2.MeshInstanceCount ()); assert.strictEqual (model.MeshInstanceCount (), model2.MeshInstanceCount ());
assert.strictEqual (model.TriangleCount (), model2.TriangleCount ()); assert.strictEqual (model.TriangleCount (), model2.TriangleCount ());
CheckModelBounds (model, model2);
let modelBounds = OV.GetBoundingBox (model);
let model2Bounds = OV.GetBoundingBox (model2);
assert.ok (OV.CoordIsEqual3D (modelBounds.min, model2Bounds.min));
assert.ok (OV.CoordIsEqual3D (modelBounds.max, model2Bounds.max));
} }
describe ('Export-Import Test', function () { describe ('Export-Import Test', function () {
@ -221,6 +221,17 @@ describe ('Export-Import Test', function () {
done (); done ();
}); });
}); });
it ('Export-Import Bim', function (done) {
let model = CreateTestModel ();
ExportImport (model, OV.FileFormat.Text, 'bim', (model2) => {
assert.strictEqual (model2.MaterialCount (), 2);
assert.strictEqual (model.MeshInstanceCount (), model2.MeshInstanceCount ());
assert.strictEqual (model.TriangleCount (), model2.TriangleCount ());
CheckModelBounds (model, model2);
done ();
});
});
}); });
describe ('Export-Import Vertex Colors Test', function () { describe ('Export-Import Vertex Colors Test', function () {

View File

@ -5,7 +5,7 @@ export default function suite ()
{ {
describe ('Property Test', function () { describe ('Property Test', function () {
it ('Property Group', function() { it ('Property group', function() {
let group = new OV.PropertyGroup ('Group'); let group = new OV.PropertyGroup ('Group');
group.AddProperty (new OV.Property (OV.PropertyType.Text, 'name 01', 'value 01')); group.AddProperty (new OV.Property (OV.PropertyType.Text, 'name 01', 'value 01'));
group.AddProperty (new OV.Property (OV.PropertyType.Integer, 'name 02', 2)); group.AddProperty (new OV.Property (OV.PropertyType.Integer, 'name 02', 2));
@ -19,7 +19,7 @@ describe ('Property Test', function () {
assert.strictEqual (group.GetProperty (2).value, 3.5); assert.strictEqual (group.GetProperty (2).value, 3.5);
}); });
it ('Model Properties', function() { it ('Model properties', function() {
let model = new OV.Model (); let model = new OV.Model ();
let group1 = new OV.PropertyGroup ('Group 01'); let group1 = new OV.PropertyGroup ('Group 01');
let group2 = new OV.PropertyGroup ('Group 02'); let group2 = new OV.PropertyGroup ('Group 02');
@ -41,6 +41,69 @@ describe ('Property Test', function () {
assert.strictEqual (model.GetPropertyGroup (2).GetProperty (0).name, 'name 03'); assert.strictEqual (model.GetPropertyGroup (2).GetProperty (0).name, 'name 03');
assert.strictEqual (model.GetPropertyGroup (2).GetProperty (0).value, 3.5); assert.strictEqual (model.GetPropertyGroup (2).GetProperty (0).value, 3.5);
}); });
it ('Mesh properties', function() {
let mesh = new OV.Mesh ();
let group1 = new OV.PropertyGroup ('Group 01');
let group2 = new OV.PropertyGroup ('Group 02');
let group3 = new OV.PropertyGroup ('Group 03');
group1.AddProperty (new OV.Property (OV.PropertyType.Text, 'name 01', 'value 01'));
group2.AddProperty (new OV.Property (OV.PropertyType.Integer, 'name 02', 2));
group3.AddProperty (new OV.Property (OV.PropertyType.Number, 'name 03', 3.5));
mesh.AddPropertyGroup (group1);
mesh.AddPropertyGroup (group2);
mesh.AddPropertyGroup (group3);
assert.strictEqual (mesh.PropertyGroupCount (), 3);
assert.strictEqual (mesh.GetPropertyGroup (0).name, 'Group 01');
assert.strictEqual (mesh.GetPropertyGroup (1).name, 'Group 02');
assert.strictEqual (mesh.GetPropertyGroup (2).name, 'Group 03');
assert.strictEqual (mesh.GetPropertyGroup (0).GetProperty (0).name, 'name 01');
assert.strictEqual (mesh.GetPropertyGroup (0).GetProperty (0).value, 'value 01');
assert.strictEqual (mesh.GetPropertyGroup (1).GetProperty (0).name, 'name 02');
assert.strictEqual (mesh.GetPropertyGroup (1).GetProperty (0).value, 2);
assert.strictEqual (mesh.GetPropertyGroup (2).GetProperty (0).name, 'name 03');
assert.strictEqual (mesh.GetPropertyGroup (2).GetProperty (0).value, 3.5);
});
it ('Mesh clone test', function() {
let mesh = new OV.Mesh ();
let group1 = new OV.PropertyGroup ('Group 01');
let group2 = new OV.PropertyGroup ('Group 02');
let group3 = new OV.PropertyGroup ('Group 03');
group1.AddProperty (new OV.Property (OV.PropertyType.Text, 'name 01', 'value 01'));
group2.AddProperty (new OV.Property (OV.PropertyType.Integer, 'name 02', 2));
group3.AddProperty (new OV.Property (OV.PropertyType.Number, 'name 03', 3.5));
group3.AddProperty (new OV.Property (OV.PropertyType.Color, 'name 04', new OV.Color (10, 20, 30)));
mesh.AddPropertyGroup (group1);
mesh.AddPropertyGroup (group2);
mesh.AddPropertyGroup (group3);
let cloned = mesh.Clone ();
assert.strictEqual (cloned.PropertyGroupCount (), 3);
assert.strictEqual (cloned.GetPropertyGroup (0).name, 'Group 01');
assert.strictEqual (cloned.GetPropertyGroup (1).name, 'Group 02');
assert.strictEqual (cloned.GetPropertyGroup (2).name, 'Group 03');
assert.strictEqual (cloned.GetPropertyGroup (0).GetProperty (0).name, 'name 01');
assert.strictEqual (cloned.GetPropertyGroup (0).GetProperty (0).value, 'value 01');
assert.strictEqual (cloned.GetPropertyGroup (1).GetProperty (0).name, 'name 02');
assert.strictEqual (cloned.GetPropertyGroup (1).GetProperty (0).value, 2);
assert.strictEqual (cloned.GetPropertyGroup (2).GetProperty (0).name, 'name 03');
assert.strictEqual (cloned.GetPropertyGroup (2).GetProperty (0).value, 3.5);
assert.strictEqual (cloned.GetPropertyGroup (2).GetProperty (1).name, 'name 04');
assert.strictEqual (
OV.ColorToHexString (cloned.GetPropertyGroup (2).GetProperty (1).value),
OV.ColorToHexString (new OV.Color (10, 20, 30))
);
});
it ('Property to string', function() {
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Text, 'name', 'test')), 'test');
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Integer, 'name', 42)), '42');
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Number, 'name', 3.14)), '3.14');
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Boolean, 'name', true)), 'True');
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Boolean, 'name', false)), 'False');
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Percent, 'name', 0.2)), '20%');
assert.strictEqual (OV.PropertyToString (new OV.Property (OV.PropertyType.Color, 'color', new OV.Color (10, 20, 20))), '#0a1414');
});
}); });
} }

View File

@ -151,7 +151,7 @@
<td>bim</td> <td>bim</td>
<td>text</td> <td>text</td>
<td class="center green">&#x2713</td> <td class="center green">&#x2713</td>
<td class="center red">&#x2717</td> <td class="center green">&#x2713</td>
<td>Native</td> <td>Native</td>
<td></td> <td></td>
</tr> </tr>