From b180c7a7a6a718a331be334d3e672577be95247d Mon Sep 17 00:00:00 2001 From: Bui Trong Vuong Date: Fri, 14 Jun 2024 09:53:53 +0700 Subject: [PATCH] add export ifc --- packages/clay/src/base/IfcInfo.ts | 35 +++ packages/clay/src/base/IfcUnit.ts | 168 +++++++----- packages/clay/src/base/model.ts | 20 +- .../src/elements/Beam/SimpleBeam/index.ts | 2 +- .../src/elements/Beam/SimpleBeam/src/index.ts | 63 +++-- .../src/elements/Column/SimpleColumn/index.ts | 2 +- .../elements/Column/SimpleColumn/src/index.ts | 41 ++- .../CurtainWalls/SimpleCurtainWall/index.ts | 2 +- .../SimpleCurtainWall/src/index.ts | 8 + .../Elements/DynamicElementType/index.ts | 1 + .../src/elements/Elements/Element/index.ts | 27 +- .../Furniture/SimpleFurniture/index.ts | 2 +- .../Furniture/SimpleFurniture/src/index.ts | 10 +- .../elements/Members/SimpleMember/index.ts | 2 +- .../Members/SimpleMember/src/index.ts | 6 + .../elements/Openings/SimpleOpening/index.ts | 2 +- .../Openings/SimpleOpening/src/index.ts | 10 +- .../src/elements/Plates/SimplePlate/index.ts | 2 +- .../elements/Plates/SimplePlate/src/index.ts | 11 +- .../src/elements/Slabs/SimpleSlab/index.ts | 2 +- .../elements/Slabs/SimpleSlab/src/index.ts | 10 +- .../src/elements/Walls/SimpleWall/index.ts | 2 +- .../elements/Walls/SimpleWall/src/index.ts | 23 +- .../elements/Windows/SimpleWindow/index.ts | 2 +- .../Windows/SimpleWindow/src/index.ts | 8 + packages/clay/src/fragment/fragment.ts | 6 +- .../Profiles/ArbitraryClosedProfile/index.ts | 2 +- .../Profiles/IShapeProfile/index.ts | 67 +++-- src/BimModel/src/DrawTool/index.ts | 39 ++- .../src/DrawTool/src/{ => Draw}/BaseDraw.ts | 13 +- .../src/DrawTool/src/{ => Draw}/DrawArc.ts | 31 ++- .../src/DrawTool/src/{ => Draw}/DrawCircle.ts | 0 .../src/DrawTool/src/{ => Draw}/DrawLine.ts | 42 +-- .../DrawTool/src/{ => Draw}/DrawPickLine.ts | 0 .../src/DrawTool/src/{ => Draw}/DrawPoint.ts | 22 +- .../DrawTool/src/{ => Draw}/DrawPolyLines.ts | 2 +- .../src/{ => Draw}/DrawRectangular.ts | 0 src/BimModel/src/DrawTool/src/Draw/index.ts | 8 + .../src/DrawTool/src/Modify/BaseModify.ts | 112 ++++++++ .../src/DrawTool/src/Modify/ModifyCopy.ts | 100 +++++++ .../src/DrawTool/src/Modify/ModifyMove.ts | 100 +++++++ src/BimModel/src/DrawTool/src/Modify/index.ts | 3 + src/BimModel/src/DrawTool/src/index.ts | 10 +- src/BimModel/src/LevelSystem/constants.ts | 16 ++ src/BimModel/src/LevelSystem/index.ts | 14 +- src/BimModel/src/LevelSystem/types.ts | 1 + src/BimModel/src/MaterialComponent/index.ts | 5 +- .../src/Modeling/FileTabs.tsx | 13 +- .../src/Modeling/ModelingButton.tsx | 3 +- .../src/Modeling/ModelingTabs.tsx | 35 ++- .../src/Modeling/ModifyTabs/ModifyCopy.tsx | 47 ++++ .../src/Modeling/ModifyTabs/ModifyMove.tsx | 47 ++++ .../src/Modeling/ModifyTabs/ModifyTabs.tsx | 58 +++++ .../src/Modeling/ToolButton.tsx | 3 +- .../src/constants/ModelingTools.tsx | 14 +- .../src/constants/iConClass.ts | 2 + src/BimModel/src/ModelingComponent/types.ts | 8 + src/BimModel/src/ProjectComponent/index.ts | 47 +++- .../src/Component/Property/ElementTypes.tsx | 1 + .../src/project/ElementType/Beam.ts | 5 +- .../src/project/ElementType/Column.ts | 5 +- .../src/project/ElementType/Profile.ts | 48 ++-- .../src/project/IfcProject.ts | 244 +++++++++++++----- .../src/project/SpatialStructure.ts | 106 +++----- .../src/ProjectComponent/src/project/index.ts | 1 + .../src/RendererComponent/src/Camera.ts | 2 - src/BimModel/src/SelectionComponent/index.ts | 79 +++++- .../src/ContextMenu/ContextMenuPanel.tsx | 34 +++ .../src/ContextMenu/index.tsx | 17 ++ .../src/SelectionComponent/src/index.ts | 1 + src/BimModel/src/SelectionComponent/types.ts | 3 + src/BimModel/src/Signals/ElementType/index.ts | 10 +- src/BimModel/src/Signals/Modeling/index.ts | 18 +- src/BimModel/src/Signals/Project/index.ts | 2 + .../src/system/element/ElementLocation.ts | 111 +++++++- .../src/system/element/ElementUtils.ts | 73 ++++-- .../system/element/Parameter/AreaParameter.ts | 10 +- .../system/element/Parameter/BaseParameter.ts | 3 +- .../element/Parameter/BaseParameterGroup.ts | 78 +++++- .../Parameter/Column/PsetColumnLevelCommon.ts | 5 +- .../Column/QsetColumnBaseQuantity.ts | 12 - .../element/Parameter/LengthParameter.ts | 11 +- .../element/Parameter/LevelParameter.ts | 10 +- .../element/Parameter/OffsetLevelParameter.ts | 18 +- .../element/Parameter/VolumeParameter.ts | 10 +- .../element/Parameter/WeightParameter.ts | 10 +- .../Parameter/beam/PsetBeamLevelCommon.ts | 4 - .../Parameter/beam/QsetBeamBaseQuantity.ts | 30 ++- .../src/system/element/Parameter/index.ts | 3 + .../Parameter/wall/PsetWallLevelCommon.ts | 4 - .../element/Parameter/wall/QsetWallCommon.ts | 4 - src/BimModel/src/system/geometry/index.ts | 1 + .../system/geometry/location/BaseLocation.ts | 7 +- .../Components/DimensionLocationLine.tsx | 2 + .../system/geometry/location/LocationArc.ts | 7 +- .../system/geometry/location/LocationLine.ts | 18 +- .../system/geometry/location/LocationPoint.ts | 16 +- .../src/system/geometry/moving/index.ts | 67 +++++ 98 files changed, 1874 insertions(+), 547 deletions(-) rename src/BimModel/src/DrawTool/src/{ => Draw}/BaseDraw.ts (92%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawArc.ts (78%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawCircle.ts (100%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawLine.ts (83%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawPickLine.ts (100%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawPoint.ts (88%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawPolyLines.ts (93%) rename src/BimModel/src/DrawTool/src/{ => Draw}/DrawRectangular.ts (100%) create mode 100644 src/BimModel/src/DrawTool/src/Draw/index.ts create mode 100644 src/BimModel/src/DrawTool/src/Modify/BaseModify.ts create mode 100644 src/BimModel/src/DrawTool/src/Modify/ModifyCopy.ts create mode 100644 src/BimModel/src/DrawTool/src/Modify/ModifyMove.ts create mode 100644 src/BimModel/src/DrawTool/src/Modify/index.ts create mode 100644 src/BimModel/src/LevelSystem/constants.ts create mode 100644 src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyCopy.tsx create mode 100644 src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyMove.tsx create mode 100644 src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyTabs.tsx create mode 100644 src/BimModel/src/SelectionComponent/src/ContextMenu/ContextMenuPanel.tsx create mode 100644 src/BimModel/src/SelectionComponent/src/ContextMenu/index.tsx create mode 100644 src/BimModel/src/SelectionComponent/src/index.ts create mode 100644 src/BimModel/src/SelectionComponent/types.ts create mode 100644 src/BimModel/src/system/geometry/moving/index.ts diff --git a/packages/clay/src/base/IfcInfo.ts b/packages/clay/src/base/IfcInfo.ts index dcaa60d..10367e2 100644 --- a/packages/clay/src/base/IfcInfo.ts +++ b/packages/clay/src/base/IfcInfo.ts @@ -1,5 +1,6 @@ import {IFC4X3 as IFC} from "web-ifc"; import {IfcUnit} from "./IfcUnit"; +import {Model} from "./model"; const version = "0.0.1"; const application = "Web application Bim-modeling"; export class IfcInfo { @@ -144,4 +145,38 @@ export class IfcInfo { this.IfcAxis2PlacementGlobal3D, this.IfcDirectionTrueNorth3D ); + get IfcUnitAssignment() { + return this.ifcUnit.IfcUnitAssignment; + } + /** + * + */ + constructor(private model: Model) {} + export() { + this.model.set(this.IfcActorRole); + this.model.set(this.IfcAddress); + this.model.set(this.IfcOrganization); + this.model.set(this.IfcPerson); + this.model.set(this.IfcPersonAndOrganization); + this.model.set(this.IfcApplication); + this.model.set(this.IfcOwnerHistory); + this.model.set(this.IfcCartesianPointGlobal2D); + this.model.set(this.IfcCartesianPointGlobal3D); + this.model.set(this.IfcDirectionGlobal2D); + this.model.set(this.IfcDirectionGlobal3D); + this.model.set(this.IfcDirectionLocal3D); + this.model.set(this.IfcDirectionTrueNorth3D); + this.model.set(this.IfcAxisXPositive); + this.model.set(this.IfcAxisXNegative); + this.model.set(this.IfcAxisYPositive); + this.model.set(this.IfcAxisYNegative); + this.model.set(this.IfcAxisZPositive); + this.model.set(this.IfcAxisZNegative); + this.model.set(this.IfcAxis2PlacementGlobal3D); + this.model.set(this.IfcAxis2PlacementGlobal); + this.model.set(this.IfcBuildingAddress); + this.model.set(this.IfcSiteAddress); + + this.ifcUnit.export(this.model); + } } diff --git a/packages/clay/src/base/IfcUnit.ts b/packages/clay/src/base/IfcUnit.ts index af47dde..c1f6cc1 100644 --- a/packages/clay/src/base/IfcUnit.ts +++ b/packages/clay/src/base/IfcUnit.ts @@ -1,57 +1,56 @@ import {IFC4X3 as IFC} from "web-ifc"; +import {Model} from "./model"; export class IfcUnit { - private IfcAxis2PlacementGlobal3D!: IFC.IfcAxis2Placement3D; - private IfcDirectionTrueNorth3D!: IFC.IfcDirection; IfcDimensionalExponents: IFC.IfcDimensionalExponents = - new IFC.IfcDimensionalExponents(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + new IFC.IfcDimensionalExponents(1, 1, 1, 1, 1, 1, 1); IfcLengthUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.LENGTHUNIT, - IFC.IfcSIPrefix, - IFC.IfcSIUnitName + "", + IFC.IfcSIUnitName.METRE ); IfcAreaUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.AREAUNIT, - IFC.IfcSIPrefix, - IFC.IfcSIUnitName + "", + IFC.IfcSIUnitName.SQUARE_METRE ); IfcVolumeUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.VOLUMEUNIT, - IFC.IfcSIPrefix, - IFC.IfcSIUnitName + "", + IFC.IfcSIUnitName.CUBIC_METRE ); IfcPlaneAngleUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.PLANEANGLEUNIT, - new IFC.IfcSIPrefix(), - new IFC.IfcSIUnitName() + IFC.IfcSIPrefix.ATTO, + IFC.IfcSIUnitName.RADIAN ); IfcMassUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.MASSUNIT, - new IFC.IfcSIPrefix(), - new IFC.IfcSIUnitName() + IFC.IfcSIPrefix.KILO, + IFC.IfcSIUnitName.GRAM ); IfcTimeUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.TIMEUNIT, - new IFC.IfcSIPrefix(), - new IFC.IfcSIUnitName() + IFC.IfcSIPrefix.ATTO, + IFC.IfcSIUnitName.SECOND ); IfcThermoDynamicTemperatureUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.THERMODYNAMICTEMPERATUREUNIT, - new IFC.IfcSIPrefix(), - new IFC.IfcSIUnitName() + IFC.IfcSIPrefix.ATTO, + IFC.IfcSIUnitName.PASCAL ); IfcLuminousIntensityUnit: IFC.IfcSIUnit = new IFC.IfcSIUnit( this.IfcDimensionalExponents, IFC.IfcUnitEnum.LUMINOUSINTENSITYUNIT, - new IFC.IfcSIPrefix(), - new IFC.IfcSIUnitName() + IFC.IfcSIPrefix.ATTO, + IFC.IfcSIUnitName.LUMEN ); IfcMeasureWithUnit = new IFC.IfcMeasureWithUnit( @@ -74,63 +73,90 @@ export class IfcUnit { this.IfcThermoDynamicTemperatureUnit, this.IfcLuminousIntensityUnit, ]); - IfcGeometricRepresentationContext = new IFC.IfcGeometricRepresentationContext( - new IFC.IfcLabel("Model"), - new IFC.IfcLabel("0.0"), - new IFC.IfcDimensionCount(0.0), - new IFC.IfcReal(1e-3), - this.IfcAxis2PlacementGlobal3D, - this.IfcDirectionTrueNorth3D - ); - IfcGeometricRepresentationSubContext1 = - new IFC.IfcGeometricRepresentationSubContext( - new IFC.IfcLabel("Axis"), - new IFC.IfcLabel("Model"), - this.IfcAxis2PlacementGlobal3D, - this.IfcGeometricRepresentationContext, - new IFC.IfcPositiveRatioMeasure(1e-3), - IFC.IfcGeometricProjectionEnum.GRAPH_VIEW, - new IFC.IfcLabel("") - ); - IfcGeometricRepresentationSubContext2 = - new IFC.IfcGeometricRepresentationSubContext( - new IFC.IfcLabel("Body"), - new IFC.IfcLabel("Model"), - this.IfcAxis2PlacementGlobal3D, - this.IfcGeometricRepresentationContext, - new IFC.IfcPositiveRatioMeasure(1e-3), - IFC.IfcGeometricProjectionEnum.MODEL_VIEW, - new IFC.IfcLabel("") - ); - IfcGeometricRepresentationSubContext3 = - new IFC.IfcGeometricRepresentationSubContext( - new IFC.IfcLabel("Box"), - new IFC.IfcLabel("Model"), - this.IfcAxis2PlacementGlobal3D, - this.IfcGeometricRepresentationContext, - new IFC.IfcPositiveRatioMeasure(1e-3), - IFC.IfcGeometricProjectionEnum.MODEL_VIEW, - new IFC.IfcLabel("") - ); - IfcGeometricRepresentationSubContext4 = - new IFC.IfcGeometricRepresentationSubContext( - new IFC.IfcLabel("Box"), - new IFC.IfcLabel("Model"), - this.IfcAxis2PlacementGlobal3D, - this.IfcGeometricRepresentationContext, - new IFC.IfcPositiveRatioMeasure(1e-3), - IFC.IfcGeometricProjectionEnum.USERDEFINED, - new IFC.IfcLabel("") - ); + IfcGeometricRepresentationContext!: IFC.IfcGeometricRepresentationContext; + IfcGeometricRepresentationSubContext1!: IFC.IfcGeometricRepresentationSubContext; + IfcGeometricRepresentationSubContext2!: IFC.IfcGeometricRepresentationSubContext; + IfcGeometricRepresentationSubContext3!: IFC.IfcGeometricRepresentationSubContext; + IfcGeometricRepresentationSubContext4!: IFC.IfcGeometricRepresentationSubContext; /** * */ constructor( - IfcAxis2PlacementGlobal3D: IFC.IfcAxis2Placement3D, - IfcDirectionTrueNorth3D: IFC.IfcDirection + public IfcAxis2PlacementGlobal3D: IFC.IfcAxis2Placement3D, + public IfcDirectionTrueNorth3D: IFC.IfcDirection ) { - this.IfcAxis2PlacementGlobal3D = IfcAxis2PlacementGlobal3D; - this.IfcDirectionTrueNorth3D = IfcDirectionTrueNorth3D; + this.IfcGeometricRepresentationContext = + new IFC.IfcGeometricRepresentationContext( + new IFC.IfcLabel("Model"), + new IFC.IfcLabel("0.0"), + new IFC.IfcDimensionCount(0.0), + new IFC.IfcReal(1e-3), + this.IfcAxis2PlacementGlobal3D, + this.IfcDirectionTrueNorth3D + ); + this.IfcGeometricRepresentationSubContext1 = + new IFC.IfcGeometricRepresentationSubContext( + new IFC.IfcLabel("Axis"), + new IFC.IfcLabel("Model"), + this.IfcAxis2PlacementGlobal3D, + this.IfcGeometricRepresentationContext, + null, + IFC.IfcGeometricProjectionEnum.GRAPH_VIEW, + new IFC.IfcLabel("") + ); + this.IfcGeometricRepresentationSubContext2 = + new IFC.IfcGeometricRepresentationSubContext( + new IFC.IfcLabel("Body"), + new IFC.IfcLabel("Model"), + this.IfcAxis2PlacementGlobal3D, + this.IfcGeometricRepresentationContext, + null, + + IFC.IfcGeometricProjectionEnum.MODEL_VIEW, + new IFC.IfcLabel("") + ); + this.IfcGeometricRepresentationSubContext3 = + new IFC.IfcGeometricRepresentationSubContext( + new IFC.IfcLabel("Box"), + new IFC.IfcLabel("Model"), + this.IfcAxis2PlacementGlobal3D, + this.IfcGeometricRepresentationContext, + null, + IFC.IfcGeometricProjectionEnum.MODEL_VIEW, + new IFC.IfcLabel("") + ); + this.IfcGeometricRepresentationSubContext4 = + new IFC.IfcGeometricRepresentationSubContext( + new IFC.IfcLabel("Box"), + new IFC.IfcLabel("Model"), + this.IfcAxis2PlacementGlobal3D, + this.IfcGeometricRepresentationContext, + null, + + IFC.IfcGeometricProjectionEnum.USERDEFINED, + new IFC.IfcLabel("") + ); + } + export(model: Model) { + model.set(this.IfcAxis2PlacementGlobal3D!); + model.set(this.IfcDirectionTrueNorth3D!); + model.set(this.IfcDimensionalExponents); + model.set(this.IfcLengthUnit); + model.set(this.IfcAreaUnit); + model.set(this.IfcVolumeUnit); + model.set(this.IfcPlaneAngleUnit); + model.set(this.IfcMassUnit); + model.set(this.IfcTimeUnit); + model.set(this.IfcThermoDynamicTemperatureUnit); + model.set(this.IfcLuminousIntensityUnit); + model.set(this.IfcMeasureWithUnit); + model.set(this.IfcConversionBasedUnit); + model.set(this.IfcUnitAssignment); + model.set(this.IfcGeometricRepresentationContext); + model.set(this.IfcGeometricRepresentationSubContext1); + model.set(this.IfcGeometricRepresentationSubContext2); + model.set(this.IfcGeometricRepresentationSubContext3); + model.set(this.IfcGeometricRepresentationSubContext4); } } diff --git a/packages/clay/src/base/model.ts b/packages/clay/src/base/model.ts index 42c6afd..7d63f37 100644 --- a/packages/clay/src/base/model.ts +++ b/packages/clay/src/base/model.ts @@ -4,7 +4,7 @@ import {IfcLineObject} from "web-ifc"; import {IfcInfo} from "./IfcInfo"; export class Model { - ifcInfo = new IfcInfo(); + ifcInfo = new IfcInfo(this); get IfcOwnerHistory() { return this.ifcInfo.IfcOwnerHistory; } @@ -17,7 +17,6 @@ export class Model { ifcAPI = new WEBIFC.IfcAPI(); - private _context?: WEBIFC.IFC4X3.IfcRepresentationContext; private _modelID?: number; get modelID() { @@ -28,20 +27,13 @@ export class Model { } get context() { - if (this._context === undefined) { - throw new Error("Model not initialized! Call the init() method."); - } - return this._context; + return this.ifcInfo.ifcUnit.IfcGeometricRepresentationContext; } async init() { await this.ifcAPI.Init(); this.ifcAPI.SetLogLevel(WEBIFC.LogLevel.LOG_LEVEL_OFF); - this._modelID = this.ifcAPI.CreateModel({schema: WEBIFC.Schemas.IFC4X3}); - this._context = new WEBIFC.IFC4X3.IfcRepresentationContext( - new WEBIFC.IFC4X3.IfcLabel("Default"), - new WEBIFC.IFC4X3.IfcLabel("Model") - ); + this._modelID = this.ifcAPI.CreateModel({schema: "IFC4X3_ADD2"}); } set(item: WEBIFC.IfcLineObject) { @@ -96,4 +88,10 @@ export class Model { this._modelID++; this.ifcAPI.OpenModel(model); } + export() { + if (this._modelID === undefined) { + throw new Error("Malformed model!"); + } + return this.ifcAPI.SaveModel(this._modelID); + } } diff --git a/packages/clay/src/elements/Beam/SimpleBeam/index.ts b/packages/clay/src/elements/Beam/SimpleBeam/index.ts index ba4af00..ff922b8 100644 --- a/packages/clay/src/elements/Beam/SimpleBeam/index.ts +++ b/packages/clay/src/elements/Beam/SimpleBeam/index.ts @@ -15,7 +15,7 @@ export class SimpleBeamType extends DynamicElementType { const {Name, Description, ObjectType} = config; this.attributes = new IFC.IfcBeamType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Beam/SimpleBeam/src/index.ts b/packages/clay/src/elements/Beam/SimpleBeam/src/index.ts index 16d5d19..e3e80a4 100644 --- a/packages/clay/src/elements/Beam/SimpleBeam/src/index.ts +++ b/packages/clay/src/elements/Beam/SimpleBeam/src/index.ts @@ -4,14 +4,8 @@ import {IFC4X3 as IFC} from "web-ifc"; import {Model} from "../../../../base"; import {IfcUtils} from "../../../../utils/ifc-utils"; import {Element} from "../../../Elements"; -import { - Extrusion, - Profile, - ClayGeometry, - RectangleProfile, - IShapeProfile, -} from "../../../../geometries"; -import {BVH, Fragment} from "../../../../fragment"; +import {Extrusion, Profile, ClayGeometry} from "../../../../geometries"; +import {Fragment} from "../../../../fragment"; import {SimpleBeamType} from ".."; export class SimpleBeam extends Element { attributes: IFC.IfcElement; @@ -35,10 +29,9 @@ export class SimpleBeam extends Element { } get direction() { - const vector = new THREE.Vector3(); - vector.subVectors(this.endPoint, this.startPoint); - vector.normalize(); - return vector; + return new THREE.Vector3() + .subVectors(this.endPoint, this.startPoint) + .normalize(); } constructor(model: Model, type: SimpleBeamType) { @@ -52,18 +45,16 @@ export class SimpleBeam extends Element { this.geometries.add(id); const placement = IfcUtils.localPlacement(); - const shape = IfcUtils.productDefinitionShape(model, [ - this.body.attributes, - ]); + const product = IfcUtils.productDefinitionShape(model, []); this.attributes = new IFC.IfcBeam( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, placement, - shape, + product, null, null ); @@ -76,10 +67,10 @@ export class SimpleBeam extends Element { this.rotation.y = rotationY; this.body.depth = this.length; this.body.update(); - const shape = this.model.get(this.attributes.Representation); - const reps = this.model.get(shape.Representations[0]); - reps.Items = [this.body.attributes]; - this.model.set(reps); + const product = this.model.get(this.attributes.Representation); + const shape = this.model.get(product.Representations[0]); + shape.Items = [this.body.attributes]; + this.model.set(shape); this.updateGeometryID(); super.update(updateGeometry); } @@ -92,19 +83,13 @@ export class SimpleBeam extends Element { this.endPoint.x = end.x; this.endPoint.y = -end.z; this.endPoint.z = end.y; + this.position = this.startPoint; this.update(true); this.updateFragment(); }; - updateDrawLine(start: THREE.Vector3, end: THREE.Vector3) { - this.startPoint.x = start.x; - this.startPoint.y = -start.z; - this.startPoint.z = start.y; - this.endPoint.x = end.x; - this.endPoint.y = -end.z; - this.endPoint.z = end.y; - this.update(true); - } - updateDrawArc(start: THREE.Vector3, end: THREE.Vector3) { + updateDraw = (update: any) => { + const {start, end} = update; + if (!start || !end) return; this.startPoint.x = start.x; this.startPoint.y = -start.z; this.startPoint.z = start.y; @@ -112,13 +97,23 @@ export class SimpleBeam extends Element { this.endPoint.y = -end.z; this.endPoint.z = end.y; this.update(true); - } - updateOffsetLevel() { + }; + updateOffsetLevel = (_update: any) => { //@ts-ignore const {height} = this.type.profile; if (!height) return; this.body.position.y = -height / 2; - } + }; + updateLevel = (_update: any) => {}; + onClone = (material: THREE.MeshLambertMaterial) => { + const element = this.type.addInstance(material); + element.startPoint = this.startPoint.clone(); + element.endPoint = this.endPoint.clone(); + element.position = element.startPoint; + element.body.position = this.body.position.clone(); + element.update(true); + return element; + }; private updateGeometryID() { const modelID = this.model.modelID; const id = this.attributes.expressID; diff --git a/packages/clay/src/elements/Column/SimpleColumn/index.ts b/packages/clay/src/elements/Column/SimpleColumn/index.ts index 4ec0cae..6913a73 100644 --- a/packages/clay/src/elements/Column/SimpleColumn/index.ts +++ b/packages/clay/src/elements/Column/SimpleColumn/index.ts @@ -19,7 +19,7 @@ export class SimpleColumnType extends DynamicElementType { this.attributes = new IFC.IfcColumnType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Column/SimpleColumn/src/index.ts b/packages/clay/src/elements/Column/SimpleColumn/src/index.ts index 44c1f1c..b78bcd3 100644 --- a/packages/clay/src/elements/Column/SimpleColumn/src/index.ts +++ b/packages/clay/src/elements/Column/SimpleColumn/src/index.ts @@ -32,7 +32,7 @@ export class SimpleColumn extends Element { this.attributes = new IFC.IfcColumn( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, @@ -44,7 +44,7 @@ export class SimpleColumn extends Element { this.model.set(this.attributes); } - updateLocation!: (update: any) => void; + update(updateGeometry = false) { this.body.depth = this.height; this.body.update(); @@ -57,7 +57,42 @@ export class SimpleColumn extends Element { this.updateGeometryID(); super.update(updateGeometry); } - + updateLocation = (update: any) => { + const {point} = update; + if (!point) return; + this.position.x = point.x; + this.position.y = -point.z; + this.position.z = point.y; + this.update(true); + this.updateFragment(); + }; + updateDraw = (update: any) => { + const {point} = update; + if (!point) return; + this.position.x = point.x; + this.position.y = -point.z; + this.position.z = point.y; + this.update(true); + }; + updateOffsetLevel = (_update: any) => { + //@ts-ignore + const {height} = this.type.profile; + if (!height) return; + this.body.position.y = -height / 2; + }; + updateLevel = (_update: any) => {}; + onClone = (material: THREE.MeshLambertMaterial) => { + const element = this.type.addInstance(material); + element.position = this.position.clone(); + element.update(true); + const {fragments, clones} = element.type; + for (const [id, fragment] of fragments) { + const clone = Fragment.clone(fragment); + clones.set(id, clone); + fragment.mesh.removeFromParent(); + } + return element; + }; private updateGeometryID() { const modelID = this.model.modelID; const id = this.attributes.expressID; diff --git a/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/index.ts b/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/index.ts index e29d35a..ad75579 100644 --- a/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/index.ts +++ b/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/index.ts @@ -76,7 +76,7 @@ export class SimpleCurtainWallType extends StaticElementType this.attributes = new IFC.IfcCurtainWallType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/src/index.ts b/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/src/index.ts index 789a8cf..dd97356 100644 --- a/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/src/index.ts +++ b/packages/clay/src/elements/CurtainWalls/SimpleCurtainWall/src/index.ts @@ -1,3 +1,5 @@ +import * as THREE from "three"; + import {IFC4X3 as IFC} from "web-ifc"; import {v4 as uuidv4} from "uuid"; import {Model} from "../../../../base"; @@ -35,4 +37,10 @@ export class SimpleCurtainWall extends Element { this.model.set(this.attributes); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/elements/Elements/DynamicElementType/index.ts b/packages/clay/src/elements/Elements/DynamicElementType/index.ts index 33c34e9..f20b749 100644 --- a/packages/clay/src/elements/Elements/DynamicElementType/index.ts +++ b/packages/clay/src/elements/Elements/DynamicElementType/index.ts @@ -7,6 +7,7 @@ export abstract class DynamicElementType< T extends Element > extends ElementType { abstract attributes: IFC.IfcElementType; + get name() { return this.attributes.Name?.value; } diff --git a/packages/clay/src/elements/Elements/Element/index.ts b/packages/clay/src/elements/Elements/Element/index.ts index aad3d0f..e172359 100644 --- a/packages/clay/src/elements/Elements/Element/index.ts +++ b/packages/clay/src/elements/Elements/Element/index.ts @@ -6,7 +6,7 @@ import {ClayObject, Model} from "../../../base"; import {ElementType} from "../ElementType"; import {IfcUtils} from "../../../utils/ifc-utils"; import {SimpleOpening} from "../../Openings"; -import {IndexedGeometry, FragmentMesh} from "../../../fragment"; +import {IndexedGeometry, FragmentMesh, Fragment} from "../../../fragment"; import {BVH} from "../../../fragment/bvh"; export abstract class Element extends ClayObject { @@ -52,7 +52,10 @@ export abstract class Element extends ClayObject { return meshes; } abstract updateLocation: (update: any) => void; - + abstract updateDraw: (update: any) => void; + abstract updateLevel: (update: any) => void; + abstract updateOffsetLevel: (update: any) => void; + abstract onClone: (material: THREE.MeshLambertMaterial) => Element; protected constructor(model: Model, type: ElementType) { super(model); this.type = type; @@ -175,23 +178,9 @@ export abstract class Element extends ClayObject { const fragment0 = this.type.fragments.get(id); const fragment = this.type.clones.get(id); if (!fragment || !fragment0) continue; - BVH.dispose(fragment.mesh.geometry); - fragment.mesh.geometry.dispose(); - (fragment.mesh.geometry as any) = null; - const geometry = fragment0.mesh.geometry.clone(); - BVH.apply(geometry); - fragment.mesh.geometry = geometry; - const matrix = new THREE.Matrix4(); - const color = new THREE.Color(); - for (let i = 0; i < fragment0.mesh.count; i++) { - fragment0.mesh.getMatrixAt(i, matrix); - fragment.mesh.setMatrixAt(i, matrix); - if (fragment0.mesh.instanceColor) { - fragment0.mesh.getColorAt(i, color); - fragment0.mesh.setColorAt(i, color); - } - fragment.update(); - } + fragment.dispose(true); + this.type.clones.delete(id); + this.type.clones.set(id, Fragment.clone(fragment0)); } } } diff --git a/packages/clay/src/elements/Furniture/SimpleFurniture/index.ts b/packages/clay/src/elements/Furniture/SimpleFurniture/index.ts index 7a55f41..c942a76 100644 --- a/packages/clay/src/elements/Furniture/SimpleFurniture/index.ts +++ b/packages/clay/src/elements/Furniture/SimpleFurniture/index.ts @@ -31,7 +31,7 @@ export class SimpleFurnitureType extends StaticElementType { this.attributes = new IFC.IfcFurnishingElementType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Furniture/SimpleFurniture/src/index.ts b/packages/clay/src/elements/Furniture/SimpleFurniture/src/index.ts index 8e4f19b..603eb2c 100644 --- a/packages/clay/src/elements/Furniture/SimpleFurniture/src/index.ts +++ b/packages/clay/src/elements/Furniture/SimpleFurniture/src/index.ts @@ -1,3 +1,5 @@ +import * as THREE from "three"; + import {IFC4X3 as IFC} from "web-ifc"; import {v4 as uuidv4} from "uuid"; import {Model} from "../../../../base"; @@ -20,7 +22,7 @@ export class SimpleFurniture extends Element { this.attributes = new IFC.IfcFurnishingElement( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, @@ -32,4 +34,10 @@ export class SimpleFurniture extends Element { this.model.set(this.attributes); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/elements/Members/SimpleMember/index.ts b/packages/clay/src/elements/Members/SimpleMember/index.ts index 0de275d..aa34343 100644 --- a/packages/clay/src/elements/Members/SimpleMember/index.ts +++ b/packages/clay/src/elements/Members/SimpleMember/index.ts @@ -25,7 +25,7 @@ export class SimpleMemberType extends DynamicElementType { this.attributes = new IFC.IfcMemberType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Members/SimpleMember/src/index.ts b/packages/clay/src/elements/Members/SimpleMember/src/index.ts index 4114dd1..d3def42 100644 --- a/packages/clay/src/elements/Members/SimpleMember/src/index.ts +++ b/packages/clay/src/elements/Members/SimpleMember/src/index.ts @@ -49,4 +49,10 @@ export class SimpleMember extends Element { this.model.set(this.attributes); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/elements/Openings/SimpleOpening/index.ts b/packages/clay/src/elements/Openings/SimpleOpening/index.ts index f4103b9..0f32a2a 100644 --- a/packages/clay/src/elements/Openings/SimpleOpening/index.ts +++ b/packages/clay/src/elements/Openings/SimpleOpening/index.ts @@ -41,7 +41,7 @@ export class SimpleOpeningType extends StaticElementType { this.attributes = new IFC.IfcElementType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Openings/SimpleOpening/src/index.ts b/packages/clay/src/elements/Openings/SimpleOpening/src/index.ts index 718466f..7868dd6 100644 --- a/packages/clay/src/elements/Openings/SimpleOpening/src/index.ts +++ b/packages/clay/src/elements/Openings/SimpleOpening/src/index.ts @@ -1,3 +1,5 @@ +import * as THREE from "three"; + import {IFC4X3 as IFC} from "web-ifc"; import {v4 as uuidv4} from "uuid"; import {Element} from "../../../Elements/Element"; @@ -20,7 +22,7 @@ export class SimpleOpening extends Element { this.attributes = new IFC.IfcOpeningElement( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, @@ -33,4 +35,10 @@ export class SimpleOpening extends Element { this.model.set(this.attributes); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/elements/Plates/SimplePlate/index.ts b/packages/clay/src/elements/Plates/SimplePlate/index.ts index 161172c..e780c17 100644 --- a/packages/clay/src/elements/Plates/SimplePlate/index.ts +++ b/packages/clay/src/elements/Plates/SimplePlate/index.ts @@ -26,7 +26,7 @@ export class SimplePlateType extends DynamicElementType { this.attributes = new IFC.IfcPlateType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Plates/SimplePlate/src/index.ts b/packages/clay/src/elements/Plates/SimplePlate/src/index.ts index cd9322c..31e07ce 100644 --- a/packages/clay/src/elements/Plates/SimplePlate/src/index.ts +++ b/packages/clay/src/elements/Plates/SimplePlate/src/index.ts @@ -1,3 +1,5 @@ +import * as THREE from "three"; + import {IFC4X3 as IFC} from "web-ifc"; import {Element} from "../../../Elements"; import {Model} from "../../../../base"; @@ -5,7 +7,6 @@ import {SimplePlateType} from ".."; import {v4 as uuidv4} from "uuid"; import {IfcUtils} from "../../../../utils/ifc-utils"; import {Extrusion, RectangleProfile} from "../../../../geometries"; -import * as THREE from "three"; export class SimplePlate extends Element { attributes: IFC.IfcPlate; @@ -34,7 +35,7 @@ export class SimplePlate extends Element { this.attributes = new IFC.IfcPlate( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, @@ -47,4 +48,10 @@ export class SimplePlate extends Element { this.model.set(this.attributes); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/elements/Slabs/SimpleSlab/index.ts b/packages/clay/src/elements/Slabs/SimpleSlab/index.ts index 7febccc..829b0e7 100644 --- a/packages/clay/src/elements/Slabs/SimpleSlab/index.ts +++ b/packages/clay/src/elements/Slabs/SimpleSlab/index.ts @@ -17,7 +17,7 @@ export class SimpleSlabType extends DynamicElementType { this.attributes = new IFC.IfcSlabType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Slabs/SimpleSlab/src/index.ts b/packages/clay/src/elements/Slabs/SimpleSlab/src/index.ts index ce66357..35060be 100644 --- a/packages/clay/src/elements/Slabs/SimpleSlab/src/index.ts +++ b/packages/clay/src/elements/Slabs/SimpleSlab/src/index.ts @@ -1,3 +1,5 @@ +import * as THREE from "three"; + import {v4 as uuidv4} from "uuid"; import {IFC4X3 as IFC} from "web-ifc"; import {Model} from "../../../../base"; @@ -31,7 +33,7 @@ export class SimpleSlab extends Element { this.attributes = new IFC.IfcSlab( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, @@ -50,4 +52,10 @@ export class SimpleSlab extends Element { super.update(updateGeometry); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/elements/Walls/SimpleWall/index.ts b/packages/clay/src/elements/Walls/SimpleWall/index.ts index ba0692d..7d626aa 100644 --- a/packages/clay/src/elements/Walls/SimpleWall/index.ts +++ b/packages/clay/src/elements/Walls/SimpleWall/index.ts @@ -17,7 +17,7 @@ export class SimpleWallType extends DynamicElementType { this.attributes = new IFC.IfcWallType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Walls/SimpleWall/src/index.ts b/packages/clay/src/elements/Walls/SimpleWall/src/index.ts index babc318..60c1eab 100644 --- a/packages/clay/src/elements/Walls/SimpleWall/src/index.ts +++ b/packages/clay/src/elements/Walls/SimpleWall/src/index.ts @@ -71,7 +71,7 @@ export class SimpleWall extends Element { this.attributes = new IFC.IfcWall( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, null, null, null, @@ -95,6 +95,27 @@ export class SimpleWall extends Element { this.update(true, true); this.updateFragment(); }; + updateDraw = (update: any) => { + const {start, end} = update; + if (!start || !end) return; + this.startPoint.x = start.x; + this.startPoint.y = -start.z; + this.startPoint.z = start.y; + this.endPoint.x = end.x; + this.endPoint.y = -end.z; + this.endPoint.z = end.y; + this.update(true); + }; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (material: THREE.MeshLambertMaterial) => { + const element = this.type.addInstance(material); + element.startPoint = this.startPoint.clone(); + element.endPoint = this.endPoint.clone(); + element.position = element.startPoint; + element.update(true); + return element; + }; update(updateGeometry = false, updateCorners = false) { this.updateAllOpenings(); diff --git a/packages/clay/src/elements/Windows/SimpleWindow/index.ts b/packages/clay/src/elements/Windows/SimpleWindow/index.ts index cbf1cd3..58980c6 100644 --- a/packages/clay/src/elements/Windows/SimpleWindow/index.ts +++ b/packages/clay/src/elements/Windows/SimpleWindow/index.ts @@ -78,7 +78,7 @@ export class SimpleWindowType extends StaticElementType { this.attributes = new IFC.IfcWindowType( new IFC.IfcGloballyUniqueId(uuidv4()), - this.model.IfcOwnerHistory, + null, new IFC.IfcLabel(Name), new IFC.IfcLabel(Description), new IFC.IfcLabel(ObjectType), diff --git a/packages/clay/src/elements/Windows/SimpleWindow/src/index.ts b/packages/clay/src/elements/Windows/SimpleWindow/src/index.ts index 15499b9..cc27685 100644 --- a/packages/clay/src/elements/Windows/SimpleWindow/src/index.ts +++ b/packages/clay/src/elements/Windows/SimpleWindow/src/index.ts @@ -1,3 +1,5 @@ +import * as THREE from "three"; + import {IFC4X3 as IFC} from "web-ifc"; import {v4 as uuidv4} from "uuid"; import {Model} from "../../../../base"; @@ -39,4 +41,10 @@ export class SimpleWindow extends Element { this.model.set(this.attributes); } updateLocation!: (update: any) => void; + updateDraw = (_update: any) => {}; + updateOffsetLevel = (_update: any) => {}; + updateLevel = (_update: any) => {}; + onClone = (_material: THREE.MeshLambertMaterial) => { + return this; + }; } diff --git a/packages/clay/src/fragment/fragment.ts b/packages/clay/src/fragment/fragment.ts index 694cbed..f1272b8 100644 --- a/packages/clay/src/fragment/fragment.ts +++ b/packages/clay/src/fragment/fragment.ts @@ -440,9 +440,9 @@ export class Fragment { const geometry = mesh.geometry.clone(); BVH.apply(geometry); const clone = new Fragment(geometry, mesh.material, mesh.count); - clone.instanceToItem = instanceToItem; - clone.itemToInstances = itemToInstances; - clone.ids = ids; + clone.instanceToItem = new Map(instanceToItem); + clone.itemToInstances = new Map(itemToInstances); + clone.ids = new Set(Array.from(ids)); clone.capacity = capacity; const matrix = new THREE.Matrix4(); const color = new THREE.Color(); diff --git a/packages/clay/src/geometries/Profiles/ArbitraryClosedProfile/index.ts b/packages/clay/src/geometries/Profiles/ArbitraryClosedProfile/index.ts index 1ffde94..16c39ea 100644 --- a/packages/clay/src/geometries/Profiles/ArbitraryClosedProfile/index.ts +++ b/packages/clay/src/geometries/Profiles/ArbitraryClosedProfile/index.ts @@ -20,7 +20,7 @@ export class ArbitraryClosedProfile extends Profile { this.attributes = new IFC.IfcArbitraryClosedProfileDef( IFC.IfcProfileTypeEnum.CURVE, - null, + new IFC.IfcLabel(""), new IFC.IfcPolyline([]) ); this.model.set(this.attributes); diff --git a/packages/clay/src/geometries/Profiles/IShapeProfile/index.ts b/packages/clay/src/geometries/Profiles/IShapeProfile/index.ts index c9631d0..ad36973 100644 --- a/packages/clay/src/geometries/Profiles/IShapeProfile/index.ts +++ b/packages/clay/src/geometries/Profiles/IShapeProfile/index.ts @@ -1,7 +1,10 @@ import * as THREE from "three"; +import {IFC4X3 as IFC} from "web-ifc"; + import {Model} from "../../../base"; import {ArbitraryClosedProfile} from "../ArbitraryClosedProfile"; import {IIfcBaseConfig} from "../../../elements"; +import {Profile} from ".."; export interface IIShapeConfig { bw: number; @@ -10,35 +13,55 @@ export interface IIShapeConfig { hf: number; } -export class IShapeProfile extends ArbitraryClosedProfile { +export class IShapeProfile extends Profile { + attributes: IFC.IfcProfileDef; + update = () => { + // + }; + updateProfile = (update: any) => { + console.log(update); + }; height = 0; width = 0; constructor(model: Model, config: IIShapeConfig) { super(model); - const {bw, hw, hf} = config; + const {bf, hw, hf, bw} = config; this.height = hw + hf * 2; - this.width = bw; - this.updateProfile(this.updateIShape(config)); - } - private updateIShape(update: IIShapeConfig) { - const {bw, hw, bf, hf} = update; - const points: THREE.Vector3[] = []; - points.push(new THREE.Vector3(bf / 2, hw / 2 + hf, 0)); - points.push(new THREE.Vector3(bf / 2, hw / 2, 0)); - points.push(new THREE.Vector3(bw / 2, hw / 2, 0)); - points.push(new THREE.Vector3(bw / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(bf / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(bf / 2, -hw / 2 - hf, 0)); - points.push(new THREE.Vector3(-bf / 2, -hw / 2 - hf, 0)); - points.push(new THREE.Vector3(-bf / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(-bw / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(-bw / 2, hw / 2, 0)); - points.push(new THREE.Vector3(-bf / 2, hw / 2, 0)); - points.push(new THREE.Vector3(-bf / 2, hw / 2 + hf, 0)); - points.push(new THREE.Vector3(bf / 2, hw / 2 + hf, 0)); - return points; + this.width = bf; + const name = `I${this.height}x${this.width}`; + this.attributes = new IFC.IfcIShapeProfileDef( + IFC.IfcProfileTypeEnum.AREA, + new IFC.IfcLabel(name), + null, + new IFC.IfcPositiveLengthMeasure(this.width), + new IFC.IfcPositiveLengthMeasure(this.height), + new IFC.IfcPositiveLengthMeasure(bw), + new IFC.IfcPositiveLengthMeasure(hf), + null, + null, + null + ); + this.model.set(this.attributes); } + // private updateIShape(update: IIShapeConfig) { + // const {bw, hw, bf, hf} = update; + // const points: THREE.Vector3[] = []; + // points.push(new THREE.Vector3(bf / 2, hw / 2 + hf, 0)); + // points.push(new THREE.Vector3(bf / 2, hw / 2, 0)); + // points.push(new THREE.Vector3(bw / 2, hw / 2, 0)); + // points.push(new THREE.Vector3(bw / 2, -hw / 2, 0)); + // points.push(new THREE.Vector3(bf / 2, -hw / 2, 0)); + // points.push(new THREE.Vector3(bf / 2, -hw / 2 - hf, 0)); + // points.push(new THREE.Vector3(-bf / 2, -hw / 2 - hf, 0)); + // points.push(new THREE.Vector3(-bf / 2, -hw / 2, 0)); + // points.push(new THREE.Vector3(-bw / 2, -hw / 2, 0)); + // points.push(new THREE.Vector3(-bw / 2, hw / 2, 0)); + // points.push(new THREE.Vector3(-bf / 2, hw / 2, 0)); + // points.push(new THREE.Vector3(-bf / 2, hw / 2 + hf, 0)); + // points.push(new THREE.Vector3(bf / 2, hw / 2 + hf, 0)); + // return points; + // } getInfo = () => { return {} as IIfcBaseConfig; }; diff --git a/src/BimModel/src/DrawTool/index.ts b/src/BimModel/src/DrawTool/index.ts index d9b2fa9..716471e 100644 --- a/src/BimModel/src/DrawTool/index.ts +++ b/src/BimModel/src/DrawTool/index.ts @@ -5,6 +5,7 @@ import * as THREE from "three"; import { Components, + MovingLine, RaycasterComponent, SelectionComponent, } from "@BimModel/src"; @@ -19,9 +20,16 @@ import { DrawPolyLines, DrawRectangular, DrawCircle, + BaseModify, + ModifyCopy, + ModifyMove, } from "./src"; import {effect} from "@preact/signals-react"; -import {currentLevelSignal, drawingTypeSignal} from "@BimModel/src/Signals"; +import { + currentLevelSignal, + drawingTypeSignal, + modifySignal, +} from "@BimModel/src/Signals"; import {ILevel} from "../LevelSystem/types"; export class DrawTool extends Component implements Disposable { static readonly uuid = UUID.DrawTool; @@ -33,14 +41,15 @@ export class DrawTool extends Component implements Disposable { /** @draws */ private draws: {[name: string]: BaseDraw} = {}; + private modifies: {[name: string]: BaseModify} = {}; set workPlaneLevel(level: ILevel) { const {elevation} = level; - this.workPlane = new THREE.Plane().setFromNormalAndCoplanarPoint( + this.workPlane.setFromNormalAndCoplanarPoint( DrawTool.upDirection, new THREE.Vector3(0, elevation, 0) ); } - + movingLine = new MovingLine(this.components); get RaycasterComponent() { return this.components.tools.get(RaycasterComponent); } @@ -55,6 +64,7 @@ export class DrawTool extends Component implements Disposable { super(components); this.components.tools.add(DrawTool.uuid, this); this.initDraws(); + this.initModify(); effect(() => { for (const name in this.draws) { const draw = this.draws[name]; @@ -68,6 +78,19 @@ export class DrawTool extends Component implements Disposable { this.RaycasterComponent!.visibleInfo = drawingTypeSignal.value !== "None"; this.SelectionComponent.setupEvent = drawingTypeSignal.value === "None"; }); + effect(() => { + for (const name in this.modifies) { + const modify = this.modifies[name]; + if (modifySignal.value === null) { + modify.setupEvent = false; + modify.dispose(); + } else { + modify.setupEvent = modifySignal.value === name; + } + } + this.RaycasterComponent!.visibleInfo = modifySignal.value !== null; + this.SelectionComponent.setupEvent = modifySignal.value === null; + }); effect(() => { if (!currentLevelSignal.value) return; this.workPlaneLevel = currentLevelSignal.value; @@ -78,6 +101,12 @@ export class DrawTool extends Component implements Disposable { this.draws[name]?.dispose(); } this.draws = {}; + for (const name in this.modifies) { + this.modifies[name]?.dispose(); + } + this.modifies = {}; + this.movingLine?.dispose(); + (this.movingLine as any) = null; } get() { @@ -98,5 +127,9 @@ export class DrawTool extends Component implements Disposable { this.draws["Point"] = new DrawPoint(this.components, this.workPlane); this.draws["PickLine"] = new DrawPickLine(this.components, this.workPlane); } + private initModify() { + this.modifies["Copy"] = new ModifyCopy(this.components); + this.modifies["Move"] = new ModifyMove(this.components); + } } ToolComponent.libraryUUIDs.add(DrawTool.uuid); diff --git a/src/BimModel/src/DrawTool/src/BaseDraw.ts b/src/BimModel/src/DrawTool/src/Draw/BaseDraw.ts similarity index 92% rename from src/BimModel/src/DrawTool/src/BaseDraw.ts rename to src/BimModel/src/DrawTool/src/Draw/BaseDraw.ts index 00cac24..e3e82ab 100644 --- a/src/BimModel/src/DrawTool/src/BaseDraw.ts +++ b/src/BimModel/src/DrawTool/src/Draw/BaseDraw.ts @@ -6,14 +6,11 @@ import { Components, RaycasterComponent, lengthUnitSignal, - DrawTool, getLocalVectorOnFace, LocationPoint, LocationArc, LocationLine, ProjectComponent, - modelStructureSignal, - modelingSignal, SelectionComponent, Snapper, } from "@BimModel/src"; @@ -59,7 +56,7 @@ export abstract class BaseDraw { return this.RaycasterComponent.currentCamera; } get container() { - return this.components.container; + return this.components.canvas; } private _setupEvent = false; @@ -111,14 +108,6 @@ export abstract class BaseDraw { } mousedown = false; - get CurrentElementIndex(): number { - if (!modelStructureSignal.value || !modelingSignal.value) return -1; - const {type, discipline} = modelingSignal.value; - const children = - modelStructureSignal.value.children[discipline].children[type].children; - return Object.keys(children).length; - } - /** * */ diff --git a/src/BimModel/src/DrawTool/src/DrawArc.ts b/src/BimModel/src/DrawTool/src/Draw/DrawArc.ts similarity index 78% rename from src/BimModel/src/DrawTool/src/DrawArc.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawArc.ts index 9f16c38..e257f7b 100644 --- a/src/BimModel/src/DrawTool/src/DrawArc.ts +++ b/src/BimModel/src/DrawTool/src/Draw/DrawArc.ts @@ -5,8 +5,8 @@ import * as THREE from "three"; import {Components} from "@BimModel/src/Components"; import {BaseDraw} from "./BaseDraw"; import {LocationArc, LocationLine, LocationPoint} from "@system/geometry"; -import {getDirection} from "@BimModel/src/utils"; import {IDrawType} from "@ModelingComponent/types"; +import {isOrthoSignal, lengthUnitSignal} from "@BimModel/src/Signals"; export class DrawArc extends BaseDraw { drawType: IDrawType = "Arc"; @@ -41,6 +41,17 @@ export class DrawArc extends BaseDraw { this.RaycasterComponent!.updateInfo(this.foundPoint); if (this.count === 0) return; this.movingPoint = this.foundPoint.clone(); + if (this.count === 1) { + if (isOrthoSignal.value) { + this.movingPoint = this.getOrtho( + this.start, + this.foundPoint.clone() + ) as THREE.Vector3; + } else { + this.movingPoint = this.foundPoint.clone(); + this.orthoDir = null; + } + } // toggle visibility to true if (!this.locationArc) this.locationArc = new LocationArc(this.components, this.workPlane); @@ -59,20 +70,28 @@ export class DrawArc extends BaseDraw { }; onKeyDown = (_e: KeyboardEvent) => { if (_e.key === "Enter") { - if (this.count === 0 || this.count % 2 === 0) { + if (this.count === 0) { this.inputKey = ""; return; } + if (!this.locationArc) return; const distance = this.getInputKey(); if (!distance) { this.inputKey = ""; return; } - // if (this.tempLocation) { - - // } + const {factor} = lengthUnitSignal.value; + if (this.count === 1) { + this.end = this.getDistance( + this.start.clone(), + this.movingPoint, + distance / factor + ); + this.locationArc.update2PointsArc(this.start, this.end); + this.count = 2; + } } - if (_e.key >= "0" && _e.key <= "9") { + if ((_e.key >= "0" && _e.key <= "9") || _e.key === ".") { this.inputKey += _e.key; } }; diff --git a/src/BimModel/src/DrawTool/src/DrawCircle.ts b/src/BimModel/src/DrawTool/src/Draw/DrawCircle.ts similarity index 100% rename from src/BimModel/src/DrawTool/src/DrawCircle.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawCircle.ts diff --git a/src/BimModel/src/DrawTool/src/DrawLine.ts b/src/BimModel/src/DrawTool/src/Draw/DrawLine.ts similarity index 83% rename from src/BimModel/src/DrawTool/src/DrawLine.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawLine.ts index f268e1f..2a52dcd 100644 --- a/src/BimModel/src/DrawTool/src/DrawLine.ts +++ b/src/BimModel/src/DrawTool/src/Draw/DrawLine.ts @@ -7,6 +7,7 @@ import {IFC4X3 as IFC} from "web-ifc"; import { Components, isOrthoSignal, + lengthUnitSignal, modelingSignal, modelStructureSignal, tempElementSignal, @@ -93,9 +94,14 @@ export class DrawLine extends BaseDraw { this.inputKey = ""; return; } + const {factor} = lengthUnitSignal.value; const start = this.points[this.points.length - 1]; - const end = this.getDistance(start, this.end, distance); - if (this.locationLine) this.locationLine.update(start, end); + const end = this.getDistance(start, this.end, distance / factor); + if (this.locationLine) { + this.locationLine.update(start, end); + this.updateElement(this.locationLine); + this.locationLine.visible = false; + } this.onFinished(); this.start = end.clone(); this.points.push(this.start); @@ -107,7 +113,7 @@ export class DrawLine extends BaseDraw { } }; onFinished = () => { - if (this.locationLine) this.locationLine.visible = false; + this.addElement(); this.inputKey = ""; this.count = 0; this.points = []; @@ -130,20 +136,23 @@ export class DrawLine extends BaseDraw { return; const {type} = modelingSignal.value; const bimElementTypes = {...tempElementSignal.value.bimElementTypes}; - const element = this.ProjectComponent.setElement( + const elementLocation = this.ProjectComponent.setElement( type, bimElementTypes, this.tempElement, this.locationLine ); - element.groupParameter = {...tempElementSignal.value.groupParameter}; + elementLocation.groupParameter = { + ...tempElementSignal.value.groupParameter, + }; + this.ProjectComponent.ifcProject.addElementLevel = elementLocation; switch (type) { case "Structure Beam": - element.addQsetBeamCommon(); + elementLocation.addQsetBeamCommon(); break; case "Wall": case "Structure Wall": - element.addQsetWallCommon(); + elementLocation.addQsetWallCommon(); break; default: break; @@ -156,6 +165,9 @@ export class DrawLine extends BaseDraw { const {selectType} = tempElementSignal.value.bimElementTypes; if (!selectType) return; const {type} = modelingSignal.value; + const currentElementIndex = Object.keys( + this.ProjectComponent.elements + ).length; switch (type) { case "Structure Beam": if (!this.tempElement) { @@ -163,9 +175,9 @@ export class DrawLine extends BaseDraw { this.MaterialComponent.materialCategories[type]! ) as SimpleBeam; this.tempElement.attributes.Name = new IFC.IfcLabel( - `${type} ${this.CurrentElementIndex + 1}` + `${type} ${currentElementIndex + 1}` ); - (this.tempElement as SimpleBeam).updateOffsetLevel(); + (this.tempElement as SimpleBeam).updateOffsetLevel({}); } break; case "Wall": @@ -176,7 +188,7 @@ export class DrawLine extends BaseDraw { ) as SimpleWall; this.tempElement.attributes.Name = new IFC.IfcLabel( - `${type} ${this.CurrentElementIndex + 1}` + `${type} ${currentElementIndex + 1}` ); } break; @@ -198,17 +210,9 @@ export class DrawLine extends BaseDraw { const {start, end} = location.location; switch (type) { case "Structure Beam": - (this.tempElement as SimpleBeam).updateDrawLine(start, end); - break; case "Wall": case "Structure Wall": - (this.tempElement as SimpleWall).startPoint.x = start.x; - (this.tempElement as SimpleWall).startPoint.y = -start.z; - (this.tempElement as SimpleWall).startPoint.z = start.y; - (this.tempElement as SimpleWall).endPoint.x = end.x; - (this.tempElement as SimpleWall).endPoint.y = -end.z; - (this.tempElement as SimpleWall).endPoint.z = end.y; - (this.tempElement as SimpleWall).update(true, true); + this.tempElement.updateDraw({start, end}); break; default: break; diff --git a/src/BimModel/src/DrawTool/src/DrawPickLine.ts b/src/BimModel/src/DrawTool/src/Draw/DrawPickLine.ts similarity index 100% rename from src/BimModel/src/DrawTool/src/DrawPickLine.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawPickLine.ts diff --git a/src/BimModel/src/DrawTool/src/DrawPoint.ts b/src/BimModel/src/DrawTool/src/Draw/DrawPoint.ts similarity index 88% rename from src/BimModel/src/DrawTool/src/DrawPoint.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawPoint.ts index ed6fec1..e816f9b 100644 --- a/src/BimModel/src/DrawTool/src/DrawPoint.ts +++ b/src/BimModel/src/DrawTool/src/Draw/DrawPoint.ts @@ -32,7 +32,7 @@ export class DrawPoint extends BaseDraw { const point = this.foundPoint.clone(); if (this.Snapper.snap) point.copy(this.Snapper.snap); if (!this.locationPoint) - this.locationPoint = new LocationPoint(this.components); + this.locationPoint = new LocationPoint(this.components, this.workPlane); this.locationPoint.update(point); this.locationPoint.visible = true; this.createElement(); @@ -80,13 +80,16 @@ export class DrawPoint extends BaseDraw { return; const {type} = modelingSignal.value; const bimElementTypes = {...tempElementSignal.value.bimElementTypes}; - const element = this.ProjectComponent.setElement( + const elementLocation = this.ProjectComponent.setElement( type, bimElementTypes, this.tempElement, this.locationPoint ); - element.groupParameter = {...tempElementSignal.value.groupParameter}; + elementLocation.groupParameter = { + ...tempElementSignal.value.groupParameter, + }; + this.ProjectComponent.ifcProject.addElementLevel = elementLocation; switch (type) { case "Structure Beam": case "Structure Wall": @@ -95,11 +98,12 @@ export class DrawPoint extends BaseDraw { case "ReinForcement": break; case "Structure Column": - element.addQsetColumnCommon(); + elementLocation.addQsetColumnCommon(); break; default: break; } + this.locationPoint.visible = false; (this.tempElement as any) = null; (this.locationPoint as any) = null; }; @@ -108,6 +112,9 @@ export class DrawPoint extends BaseDraw { const {selectType} = tempElementSignal.value.bimElementTypes; if (!selectType) return; const {type} = modelingSignal.value; + const currentElementIndex = Object.keys( + this.ProjectComponent.elements + ).length; switch (type) { case "Structure Beam": case "Structure Wall": @@ -121,7 +128,7 @@ export class DrawPoint extends BaseDraw { this.MaterialComponent.materialCategories[type]! ) as SimpleColumn; this.tempElement.attributes.Name = new IFC.IfcLabel( - `${type} ${this.CurrentElementIndex + 1}` + `${type} ${currentElementIndex + 1}` ); } break; @@ -147,10 +154,7 @@ export class DrawPoint extends BaseDraw { case "ReinForcement": break; case "Structure Column": - this.tempElement.position.x = this.locationPoint.location.point.x; - this.tempElement.position.y = -this.locationPoint.location.point.z; - this.tempElement.position.z = this.locationPoint.location.point.y; - this.tempElement.update(true); + this.tempElement.updateDraw(this.locationPoint.location); break; default: break; diff --git a/src/BimModel/src/DrawTool/src/DrawPolyLines.ts b/src/BimModel/src/DrawTool/src/Draw/DrawPolyLines.ts similarity index 93% rename from src/BimModel/src/DrawTool/src/DrawPolyLines.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawPolyLines.ts index 34725fc..b9243c4 100644 --- a/src/BimModel/src/DrawTool/src/DrawPolyLines.ts +++ b/src/BimModel/src/DrawTool/src/Draw/DrawPolyLines.ts @@ -1,6 +1,6 @@ import * as THREE from "three"; -import {Components} from "@BimModel/src/Components"; +import {Components} from "@BimModel/src"; import {BaseDraw} from "./BaseDraw"; import {IDrawType} from "@ModelingComponent/types"; import {LocationArc, LocationLine, LocationPoint} from "@BimModel/src/system"; diff --git a/src/BimModel/src/DrawTool/src/DrawRectangular.ts b/src/BimModel/src/DrawTool/src/Draw/DrawRectangular.ts similarity index 100% rename from src/BimModel/src/DrawTool/src/DrawRectangular.ts rename to src/BimModel/src/DrawTool/src/Draw/DrawRectangular.ts diff --git a/src/BimModel/src/DrawTool/src/Draw/index.ts b/src/BimModel/src/DrawTool/src/Draw/index.ts new file mode 100644 index 0000000..0a89c99 --- /dev/null +++ b/src/BimModel/src/DrawTool/src/Draw/index.ts @@ -0,0 +1,8 @@ +export * from "./BaseDraw"; +export * from "./DrawLine"; +export * from "./DrawRectangular"; +export * from "./DrawPolyLines"; +export * from "./DrawArc"; +export * from "./DrawPoint"; +export * from "./DrawPickLine"; +export * from "./DrawCircle"; diff --git a/src/BimModel/src/DrawTool/src/Modify/BaseModify.ts b/src/BimModel/src/DrawTool/src/Modify/BaseModify.ts new file mode 100644 index 0000000..93604bf --- /dev/null +++ b/src/BimModel/src/DrawTool/src/Modify/BaseModify.ts @@ -0,0 +1,112 @@ +/** + * @module BaseModify + */ +import * as THREE from "three"; +import { + Components, + RaycasterComponent, + lengthUnitSignal, + getLocalVectorOnFace, + LocationPoint, + LocationArc, + LocationLine, + ProjectComponent, + modelStructureSignal, + modelingSignal, + SelectionComponent, + Snapper, + DrawTool, +} from "@BimModel/src"; + +export abstract class BaseModify { + abstract onClick: (_e: MouseEvent) => void; + abstract onMouseMove: (_e: MouseEvent) => void; + abstract onMousedown: (_e: MouseEvent) => void; + abstract onKeyDown: (_e: KeyboardEvent) => void; + abstract onFinished: () => void; + abstract dispose: () => void; + get container() { + return this.components.canvas; + } + get Snapper() { + return this.components.tools.get(Snapper); + } + get RaycasterComponent() { + return this.components.tools.get(RaycasterComponent); + } + + get movingLine() { + return this.components.tools.get(DrawTool)?.movingLine; + } + private _setupEvent = false; + set setupEvent(enabled: boolean) { + if (!this.container) return; + if (this._setupEvent === enabled) return; + this._setupEvent = enabled; + if (enabled) { + this.container.addEventListener("click", this.onClick); + this.container.addEventListener("mousemove", this.onMouseMove); + this.container.addEventListener("mousedown", this.onMousedown); + this.container.addEventListener("mouseup", this.onMouseup); + document.addEventListener("keydown", this.onKeyDown); + } else { + this.container.removeEventListener("click", this.onClick); + this.container.removeEventListener("mousemove", this.onMouseMove); + this.container.removeEventListener("mousedown", this.onMousedown); + this.container.removeEventListener("mouseup", this.onMouseup); + document.addEventListener("keydown", this.onKeyDown); + } + } + get setupEvent() { + return this._setupEvent; + } + + private _inputKey = ""; + set inputKey(inputKey: string) { + this._inputKey = inputKey; + this.RaycasterComponent.delta.textContent = `${inputKey} ${lengthUnitSignal.value.symbol}`; + } + get inputKey() { + return this._inputKey; + } + orthoDir: THREE.Vector3 | null = new THREE.Vector3(); + mousedown = false; + constructor(public components: Components) {} + private onMouseup = () => { + this.mousedown = false; + }; + getOrtho( + start: THREE.Vector3, + pMove: THREE.Vector3, + workPlane: THREE.Plane + ): THREE.Vector3 | null { + const {x, z} = getLocalVectorOnFace(workPlane.normal); + const dir = new THREE.Vector3( + pMove.x - start.x, + pMove.y - start.y, + pMove.z - start.z + ).normalize(); + let angle = dir.angleTo(x); + const dis0 = start.distanceTo(pMove); + + if ( + (angle >= 0 && angle <= Math.PI / 4) || + (angle >= (3 * Math.PI) / 4 && angle <= Math.PI) + ) { + const dis = dis0 * Math.cos(angle); + this.orthoDir = new THREE.Vector3(dis >= 0 ? 1 : -1, 0, 0); + return start.clone().add(x.clone().multiplyScalar(dis)); + } else { + angle = dir.angleTo(z); + const dis = dis0 * Math.cos(angle); + this.orthoDir = new THREE.Vector3(0, 0, dis >= 0 ? 1 : -1); + return start.clone().add(z.clone().multiplyScalar(dis)); + } + } + getInputKey() { + if (this.inputKey === "") return null; + if (this.inputKey.startsWith("0")) return null; + if (isNaN(parseFloat(this.inputKey))) return null; + return parseFloat(this.inputKey); + } +} diff --git a/src/BimModel/src/DrawTool/src/Modify/ModifyCopy.ts b/src/BimModel/src/DrawTool/src/Modify/ModifyCopy.ts new file mode 100644 index 0000000..e44b931 --- /dev/null +++ b/src/BimModel/src/DrawTool/src/Modify/ModifyCopy.ts @@ -0,0 +1,100 @@ +import * as THREE from "three"; + +import {BaseModify} from "./BaseModify"; +import { + Components, + getDirection, + isOrthoSignal, + lengthUnitSignal, + modifySignal, + selectElementSignal, +} from "@BimModel/src"; +/** + * + */ +export class ModifyCopy extends BaseModify { + private movingPoint: THREE.Vector3 | null = null; + private origin: THREE.Vector3 | null = null; + /** + * + * @param components + */ + constructor(components: Components) { + super(components); + } + onClick = (_e: MouseEvent) => { + if (!selectElementSignal.value || this.mousedown) return; + if (!this.origin || !this.movingPoint) return; + this.onFinished(); + }; + onMouseMove = (_e: MouseEvent) => { + if (!selectElementSignal.value || this.mousedown) return; + const {location} = selectElementSignal.value; + if (!location || !location.workPlane) return; + if (!this.origin) { + const pos = selectElementSignal.value.element.position; + this.origin = new THREE.Vector3(pos.x, pos.z, -pos.y); + } + this.RaycasterComponent.mouseMove = _e; + this.Snapper.find = _e; + this.movingPoint = this.RaycasterComponent.getPointRayCasPlane( + location.workPlane + ); + if (!this.movingPoint) return; + if (this.origin) { + if (isOrthoSignal.value) { + this.movingPoint = this.getOrtho( + this.origin, + this.movingPoint.clone(), + location.workPlane + ) as THREE.Vector3; + } else { + this.orthoDir = null; + } + } + if (this.Snapper.snap) this.movingPoint = this.Snapper.snap.clone(); + this.movingLine.update(this.origin, this.movingPoint); + this.movingLine.visible = true; + }; + onMousedown = (_e: MouseEvent) => { + if (_e.button === 0) this.mousedown = true; + }; + onKeyDown = (_e: KeyboardEvent) => { + if (_e.key === "Enter") { + if (!this.origin || !this.movingPoint) { + this.inputKey = ""; + return; + } + const distance = this.getInputKey(); + if (!distance) { + this.inputKey = ""; + return; + } + const {factor} = lengthUnitSignal.value; + const dir = getDirection(this.origin, this.movingPoint); + this.movingPoint = this.origin + .clone() + .add(dir.clone().multiplyScalar(distance / factor)); + this.movingLine.update(this.origin, this.movingPoint); + this.onFinished(); + } + if (_e.key >= "0" && _e.key <= "9") { + this.inputKey += _e.key; + } + }; + + onFinished = () => { + if (selectElementSignal.value && this.movingPoint && this.origin) { + selectElementSignal.value.onCopy(this.origin, this.movingPoint); + } + this.movingLine!.visible = false; + (this.movingPoint as any) = null; + (this.origin as any) = null; + modifySignal.value = null; + }; + dispose = () => { + (this.movingPoint as any) = null; + (this.origin as any) = null; + this.movingLine!.visible = false; + }; +} diff --git a/src/BimModel/src/DrawTool/src/Modify/ModifyMove.ts b/src/BimModel/src/DrawTool/src/Modify/ModifyMove.ts new file mode 100644 index 0000000..738316c --- /dev/null +++ b/src/BimModel/src/DrawTool/src/Modify/ModifyMove.ts @@ -0,0 +1,100 @@ +import * as THREE from "three"; + +import {BaseModify} from "./BaseModify"; +import { + Components, + getDirection, + isOrthoSignal, + lengthUnitSignal, + modifySignal, + selectElementSignal, +} from "@BimModel/src"; +/** + * + */ +export class ModifyMove extends BaseModify { + private movingPoint: THREE.Vector3 | null = null; + private origin: THREE.Vector3 | null = null; + /** + * + * @param components + */ + constructor(components: Components) { + super(components); + } + onClick = (_e: MouseEvent) => { + if (!selectElementSignal.value || this.mousedown) return; + if (!this.origin || !this.movingPoint) return; + this.onFinished(); + }; + onMouseMove = (_e: MouseEvent) => { + if (!selectElementSignal.value || this.mousedown) return; + const {location} = selectElementSignal.value; + if (!location || !location.workPlane) return; + if (!this.origin) { + const pos = selectElementSignal.value.element.position; + this.origin = new THREE.Vector3(pos.x, pos.z, -pos.y); + } + this.RaycasterComponent.mouseMove = _e; + this.Snapper.find = _e; + this.movingPoint = this.RaycasterComponent.getPointRayCasPlane( + location.workPlane + ); + if (!this.movingPoint) return; + if (this.origin) { + if (isOrthoSignal.value) { + this.movingPoint = this.getOrtho( + this.origin, + this.movingPoint.clone(), + location.workPlane + ) as THREE.Vector3; + } else { + this.orthoDir = null; + } + } + if (this.Snapper.snap) this.movingPoint = this.Snapper.snap.clone(); + this.movingLine.update(this.origin, this.movingPoint); + this.movingLine.visible = true; + }; + onMousedown = (_e: MouseEvent) => { + if (_e.button === 0) this.mousedown = true; + }; + onKeyDown = (_e: KeyboardEvent) => { + if (_e.key === "Enter") { + if (!this.origin || !this.movingPoint) { + this.inputKey = ""; + return; + } + const distance = this.getInputKey(); + if (!distance) { + this.inputKey = ""; + return; + } + const {factor} = lengthUnitSignal.value; + const dir = getDirection(this.origin, this.movingPoint); + this.movingPoint = this.origin + .clone() + .add(dir.clone().multiplyScalar(distance / factor)); + this.movingLine.update(this.origin, this.movingPoint); + this.onFinished(); + } + if (_e.key >= "0" && _e.key <= "9") { + this.inputKey += _e.key; + } + }; + + onFinished = () => { + if (selectElementSignal.value && this.movingPoint && this.origin) { + selectElementSignal.value.onMove(this.origin, this.movingPoint); + } + this.movingLine!.visible = false; + (this.movingPoint as any) = null; + (this.origin as any) = null; + modifySignal.value = null; + }; + dispose = () => { + (this.movingPoint as any) = null; + (this.origin as any) = null; + this.movingLine!.visible = false; + }; +} diff --git a/src/BimModel/src/DrawTool/src/Modify/index.ts b/src/BimModel/src/DrawTool/src/Modify/index.ts new file mode 100644 index 0000000..9c2a728 --- /dev/null +++ b/src/BimModel/src/DrawTool/src/Modify/index.ts @@ -0,0 +1,3 @@ +export * from "./BaseModify"; +export * from "./ModifyCopy"; +export * from "./ModifyMove"; diff --git a/src/BimModel/src/DrawTool/src/index.ts b/src/BimModel/src/DrawTool/src/index.ts index 0a89c99..b389eae 100644 --- a/src/BimModel/src/DrawTool/src/index.ts +++ b/src/BimModel/src/DrawTool/src/index.ts @@ -1,8 +1,2 @@ -export * from "./BaseDraw"; -export * from "./DrawLine"; -export * from "./DrawRectangular"; -export * from "./DrawPolyLines"; -export * from "./DrawArc"; -export * from "./DrawPoint"; -export * from "./DrawPickLine"; -export * from "./DrawCircle"; +export * from "./Draw"; +export * from "./Modify"; diff --git a/src/BimModel/src/LevelSystem/constants.ts b/src/BimModel/src/LevelSystem/constants.ts new file mode 100644 index 0000000..c85b2b5 --- /dev/null +++ b/src/BimModel/src/LevelSystem/constants.ts @@ -0,0 +1,16 @@ +import {ILevel} from "./types"; +import {v4 as uuidv4} from "uuid"; +export const defaultLevels: ILevel[] = [ + { + name: "Level 1", + index: 0, + elevation: 0.0, + uuid: uuidv4(), + }, + { + name: "Level 2", + index: 1, + elevation: 4.0, + uuid: uuidv4(), + }, +]; diff --git a/src/BimModel/src/LevelSystem/index.ts b/src/BimModel/src/LevelSystem/index.ts index 1ba873b..becd200 100644 --- a/src/BimModel/src/LevelSystem/index.ts +++ b/src/BimModel/src/LevelSystem/index.ts @@ -19,18 +19,8 @@ import {createStructureContainer, Elevation} from "./src"; import {effect} from "@preact/signals-react"; import {CubeMapComponent} from "../CubeMapComponent"; import {TransformControls} from "three/examples/jsm/controls/TransformControls"; -const defaultLevels: ILevel[] = [ - { - name: "Level 1", - index: 0, - elevation: 0.0, - }, - { - name: "Level 2", - index: 1, - elevation: 4.0, - }, -]; +import {defaultLevels} from "./constants"; + const upVector = new THREE.Vector3(0, 1, 0); const downVector = new THREE.Vector3(0, -1, 0); const upPosition = new THREE.Vector3(0, 0, 0); diff --git a/src/BimModel/src/LevelSystem/types.ts b/src/BimModel/src/LevelSystem/types.ts index 7a5de6b..3006d15 100644 --- a/src/BimModel/src/LevelSystem/types.ts +++ b/src/BimModel/src/LevelSystem/types.ts @@ -2,6 +2,7 @@ export interface ILevel { name: string; index: number; elevation: number; + uuid: string; } export type IViewType = "3D" | "Plan" | "Elevation" | "Section" | "Browsers"; export type IElevation = "South" | "West" | "East" | "North"; diff --git a/src/BimModel/src/MaterialComponent/index.ts b/src/BimModel/src/MaterialComponent/index.ts index ba0cdda..f6d2940 100644 --- a/src/BimModel/src/MaterialComponent/index.ts +++ b/src/BimModel/src/MaterialComponent/index.ts @@ -129,10 +129,13 @@ export class MaterialComponent ); this.addMaterial( "AngleMaterial", - new THREE.LineBasicMaterial({ + new THREE.LineDashedMaterial({ linewidth: 10, color: 0x34fa07, depthTest: false, + scale: 1, + dashSize: 0.2, + gapSize: 0.1, }) ); diff --git a/src/BimModel/src/ModelingComponent/src/Modeling/FileTabs.tsx b/src/BimModel/src/ModelingComponent/src/Modeling/FileTabs.tsx index d3b3d7d..ada6f67 100644 --- a/src/BimModel/src/ModelingComponent/src/Modeling/FileTabs.tsx +++ b/src/BimModel/src/ModelingComponent/src/Modeling/FileTabs.tsx @@ -1,5 +1,5 @@ -import React, {FC} from "react"; -import {iConClassName} from "../constants"; +import React from "react"; +import {buttonClassName, iConClassName} from "../constants"; //#region File import {MdOutlineCreateNewFolder as NewProject} from "react-icons/md"; import {GoFileDirectory as Open} from "react-icons/go"; @@ -17,6 +17,7 @@ import { } from "@/components/ui/tooltip"; import {Button} from "@/components/ui/button"; import { + exportIfcSignal, newProjectInfoSignal, openProjectInfoSignal, projectSignal, @@ -36,7 +37,7 @@ const FileButton = ({ + + +

Copy

+
+ + + ); +}; + +export default memo(ModifyCopy); diff --git a/src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyMove.tsx b/src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyMove.tsx new file mode 100644 index 0000000..d176f2c --- /dev/null +++ b/src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyMove.tsx @@ -0,0 +1,47 @@ +import React, {memo} from "react"; +import {IoMdMove as Move} from "react-icons/io"; +import {buttonClassName, iConClassName} from "../../constants"; +import {Button} from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import {modifySignal, selectElementSignal} from "@BimModel/src/Signals"; +import {useComputed} from "@preact/signals-react"; + +const ModifyMove = () => { + const disabled = useComputed(() => { + return ( + !selectElementSignal.value || + (modifySignal.value !== null && modifySignal.value !== "Move") + ); + }); + const active = useComputed(() => { + return modifySignal.value !== null && modifySignal.value === "Move"; + }); + return ( + + + + + + +

Move

+
+
+
+ ); +}; + +export default memo(ModifyMove); diff --git a/src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyTabs.tsx b/src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyTabs.tsx new file mode 100644 index 0000000..569422f --- /dev/null +++ b/src/BimModel/src/ModelingComponent/src/Modeling/ModifyTabs/ModifyTabs.tsx @@ -0,0 +1,58 @@ +import React, {memo} from "react"; +import {IModify, ITool} from "../../../types"; +import {buttonClassName, Modify} from "../../constants"; +import {Button} from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import {useComputed} from "@preact/signals-react"; +import {modifySignal} from "@BimModel/src/Signals"; +import ModifyCopy from "./ModifyCopy"; +import ModifyMove from "./ModifyMove"; + +const ModifyButton = ({type}: {type: ITool}) => { + const disabled = useComputed(() => { + return modifySignal.value !== null && modifySignal.value !== type.type; + }); + const active = useComputed(() => { + return modifySignal.value !== null && modifySignal.value === type.type; + }); + return ( + + + + + + +

{type.type}

+
+
+
+ ); +}; + +const ModifyTabs = () => { + return ( +
+ + + {Modify.map((type: ITool, index: number) => ( + + ))} +
+ ); +}; + +export default memo(ModifyTabs); diff --git a/src/BimModel/src/ModelingComponent/src/Modeling/ToolButton.tsx b/src/BimModel/src/ModelingComponent/src/Modeling/ToolButton.tsx index 24d9d89..96f8e84 100644 --- a/src/BimModel/src/ModelingComponent/src/Modeling/ToolButton.tsx +++ b/src/BimModel/src/ModelingComponent/src/Modeling/ToolButton.tsx @@ -9,6 +9,7 @@ import { } from "@/components/ui/tooltip"; import {useComputed} from "@preact/signals-react"; import {drawingTypeSignal} from "@BimModel/src/Signals"; +import {buttonClassName} from "../constants"; const ToolButton: FC = ({tool}) => { const disabled = useComputed(() => { @@ -29,7 +30,7 @@ const ToolButton: FC = ({tool}) => { diff --git a/src/BimModel/src/ProjectComponent/src/project/ElementType/Beam.ts b/src/BimModel/src/ProjectComponent/src/project/ElementType/Beam.ts index 07dcc4d..2f02008 100644 --- a/src/BimModel/src/ProjectComponent/src/project/ElementType/Beam.ts +++ b/src/BimModel/src/ProjectComponent/src/project/ElementType/Beam.ts @@ -7,6 +7,7 @@ import { IElementType, RectangleProfile, ArbitraryClosedProfile, + IShapeProfile, } from "clay"; export class BeamTypeUtils { @@ -14,13 +15,13 @@ export class BeamTypeUtils { model: Model, configs: { config: IIfcBaseConfig; - profile: RectangleProfile | ArbitraryClosedProfile; + profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; }[] ): IBimElementType { const types = configs.map( (config: { config: IIfcBaseConfig; - profile: RectangleProfile | ArbitraryClosedProfile; + profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; }) => { return new SimpleBeamType(model, config.config, config.profile); } diff --git a/src/BimModel/src/ProjectComponent/src/project/ElementType/Column.ts b/src/BimModel/src/ProjectComponent/src/project/ElementType/Column.ts index bfddb9d..ebbdf1f 100644 --- a/src/BimModel/src/ProjectComponent/src/project/ElementType/Column.ts +++ b/src/BimModel/src/ProjectComponent/src/project/ElementType/Column.ts @@ -7,6 +7,7 @@ import { IElementType, RectangleProfile, ArbitraryClosedProfile, + IShapeProfile, } from "clay"; export class ColumnTypeUtils { @@ -14,13 +15,13 @@ export class ColumnTypeUtils { model: Model, configs: { config: IIfcBaseConfig; - profile: RectangleProfile | ArbitraryClosedProfile; + profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; }[] ): IBimElementType { const types = configs.map( (config: { config: IIfcBaseConfig; - profile: RectangleProfile | ArbitraryClosedProfile; + profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; }) => { return new SimpleColumnType(model, config.config, config.profile); } diff --git a/src/BimModel/src/ProjectComponent/src/project/ElementType/Profile.ts b/src/BimModel/src/ProjectComponent/src/project/ElementType/Profile.ts index e2458d2..d865dc1 100644 --- a/src/BimModel/src/ProjectComponent/src/project/ElementType/Profile.ts +++ b/src/BimModel/src/ProjectComponent/src/project/ElementType/Profile.ts @@ -55,10 +55,39 @@ export class ProfileUtils { profile.updateProfile(configProfile); return profile; }; + static addRectangleConfig = ( + configProfile: IRectangleConfig, + mat = "Concrete" + ) => { + const {width, height} = configProfile; + const {factor} = lengthUnitSignal.value; + return { + Name: `R${width * factor}x${height * factor}`, + Description: mat, + ObjectType: `${mat} ${width * factor}x${height * factor}`, + }; + }; static addIShapeProfile = (configProfile: IIShapeConfig, model: Model) => { return new IShapeProfile(model, configProfile); }; + static addIShapeConfig = (configProfile: IIShapeConfig) => { + const {bf, bw, hw} = configProfile; + const {factor} = lengthUnitSignal.value; + return { + Name: `I${hw * factor}x${bf * factor}`, + Description: `SS400 I${hw * factor}x${bf * factor}`, + ObjectType: `I${hw * factor}x${bf * factor}x${bw * factor}x${ + hw * factor + }`, + }; + }; + static createRectangleProfiles(model: Model): RectangleProfile[] { + return defaultRectangleTypes.map((d) => this.addRectangleProfile(d, model)); + } + static createIShapeProfiles(model: Model): IShapeProfile[] { + return defaultIShapeTypes.map((d) => this.addIShapeProfile(d, model)); + } static createProfiles(model: Model): { config: IIfcBaseConfig; profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; @@ -92,23 +121,4 @@ export class ProfileUtils { }), ]; } - private static updateIShape(update: IIShapeConfig) { - const {bw, hw, bf, hf} = update; - const points: THREE.Vector3[] = []; - points.push(new THREE.Vector3(bf / 2, hw / 2 + hf, 0)); - points.push(new THREE.Vector3(bf / 2, hw / 2, 0)); - points.push(new THREE.Vector3(bw / 2, hw / 2, 0)); - points.push(new THREE.Vector3(bw / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(bf / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(bf / 2, -hw / 2 - hf, 0)); - points.push(new THREE.Vector3(-bf / 2, -hw / 2 - hf, 0)); - points.push(new THREE.Vector3(-bf / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(-bw / 2, -hw / 2, 0)); - points.push(new THREE.Vector3(-bw / 2, hw / 2, 0)); - points.push(new THREE.Vector3(-bf / 2, hw / 2, 0)); - points.push(new THREE.Vector3(-bf / 2, hw / 2 + hf, 0)); - points.push(new THREE.Vector3(bf / 2, hw / 2 + hf, 0)); - - return points; - } } diff --git a/src/BimModel/src/ProjectComponent/src/project/IfcProject.ts b/src/BimModel/src/ProjectComponent/src/project/IfcProject.ts index c4b7a05..36c2471 100644 --- a/src/BimModel/src/ProjectComponent/src/project/IfcProject.ts +++ b/src/BimModel/src/ProjectComponent/src/project/IfcProject.ts @@ -1,89 +1,195 @@ /** * @module ifc */ -import {Model} from "clay"; +import {IfcUtils, Model} from "clay"; +import {IFC4X3 as IFC} from "web-ifc"; +import {v4 as uuidv4} from "uuid"; import {SpatialStructure} from "./SpatialStructure"; +import {ILevel} from "@BimModel/src/LevelSystem/types"; +import {IIfcProjectConfig} from "./types"; +import {defaultLevels} from "@BimModel/src/LevelSystem/constants"; +import {ElementLocation} from "@BimModel/src/system"; +import {areaUnitSignal, lengthUnitSignal} from "@BimModel/src/Signals"; +import {effect} from "@preact/signals-react"; +const defaultProjectConfig: IIfcProjectConfig = { + Name: "Bim-modeling", + Description: "", + ObjectType: "3D Web application modeling", + Phase: "Phase 1", +}; /** *https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcProject.htm */ + export class IfcProject { + get IfcUnitAssignment() { + return this.model.ifcInfo.ifcUnit.IfcUnitAssignment; + } + get IfcOwnerHistory() { + return this.model.ifcInfo.IfcOwnerHistory; + } + get IfcSiteAddress() { + return this.model.ifcInfo.IfcSiteAddress; + } + get IfcBuildingAddress() { + return this.model.ifcInfo.IfcBuildingAddress; + } modelStructure!: SpatialStructure; - ifcBuildingStoreys: SpatialStructure[] = []; + buildingStructure!: SpatialStructure; + + set addLevel(level: ILevel) { + const ifcBuildingStorey = this.getIfcBuildingStorey(level); + const structureLevel = new SpatialStructure(ifcBuildingStorey); + structureLevel.Decomposed = + this.RelContainedInSpatialStructure(ifcBuildingStorey); + this.buildingStructure.child = structureLevel; + } + set addElementLevel(element: ElementLocation) { + if (!element.element) return; + const levelParam = element.getLevelParameter(); + if (!levelParam || !levelParam.value) return; + const buildingStorey = this.getLevel(levelParam.value); + if (!buildingStorey) return; + buildingStorey.child = new SpatialStructure(element.element.attributes); + } /** * */ constructor(private model: Model) { - // this.initIfcProject(); + this.initIfcProject(); + effect(() => { + const {symbol} = lengthUnitSignal.value; + const {IfcLengthUnit, IfcAreaUnit, IfcVolumeUnit} = + this.model.ifcInfo.ifcUnit; + if (symbol === "cm") { + IfcLengthUnit.Prefix = IFC.IfcSIPrefix.CENTI; + IfcAreaUnit.Prefix = IFC.IfcSIPrefix.CENTI; + IfcVolumeUnit.Prefix = IFC.IfcSIPrefix.CENTI; + } else if (symbol === "mm") { + IfcLengthUnit.Prefix = IFC.IfcSIPrefix.MILLI; + IfcAreaUnit.Prefix = IFC.IfcSIPrefix.MILLI; + IfcVolumeUnit.Prefix = IFC.IfcSIPrefix.MILLI; + } + }); } - // private initIfcProject() { - // const {Name, Description, ObjectType, Phase} = defaultProjectConfig; - // const ifcProject = new IFC.IfcProject( - // new IFC.IfcGloballyUniqueId(uuid4()), - // IfcOwnerHistory, - // new IFC.IfcLabel(Name), - // new IFC.IfcText(Description), - // new IFC.IfcLabel(ObjectType), - // new IFC.IfcLabel(Name), - // new IFC.IfcLabel(Phase), - // [this.model._context!], - // IfcUnitAssignment - // ); + export() { + this.model.ifcInfo.export(); + this.model.set(this.modelStructure.ifc); + this.model.set(this.modelStructure.Decomposed); + for (const [_id, child] of this.modelStructure.children) { + this.exportItem(child); + } + } + private exportItem(structure: SpatialStructure) { + this.model.set(structure.ifc); + if (!structure.Decomposed) return; + this.model.set(structure.Decomposed); + for (const [_id, child] of structure.children) { + this.exportItem(child); + } + } + private getLevel(level: ILevel) { + if (!this.buildingStructure) throw new Error(); + return this.buildingStructure.children.get(level.uuid); + } + + private initIfcProject() { + const {Name, Description, ObjectType, Phase} = defaultProjectConfig; + const ifcProject = new IFC.IfcProject( + new IFC.IfcGloballyUniqueId(uuidv4()), + this.IfcOwnerHistory, + new IFC.IfcLabel(Name), + new IFC.IfcText(Description), + new IFC.IfcLabel(ObjectType), + new IFC.IfcLabel(Name), + new IFC.IfcLabel(Phase), + [this.model.context], + this.IfcUnitAssignment + ); - // const ifcSite = new IFC.IfcSite( - // new IFC.IfcGloballyUniqueId(uuid4()), - // IfcOwnerHistory, - // new IFC.IfcLabel(""), - // new IFC.IfcLabel(""), - // new IFC.IfcLabel(""), - // new IFC.IfcObjectPlacement(null), - // null, - // new IFC.IfcLabel(""), - // IFC.IfcElementCompositionEnum.PARTIAL, - // null, - // null, - // null, - // new IFC.IfcLabel(""), - // IfcSiteAddress - // ); - // const ifcBuilding = new IFC.IfcBuilding( - // new IFC.IfcGloballyUniqueId(uuid4()), - // IfcOwnerHistory, - // new IFC.IfcLabel(""), - // new IFC.IfcLabel(""), - // new IFC.IfcLabel(""), - // new IFC.IfcObjectPlacement(null), - // null, - // new IFC.IfcLabel(""), - // IFC.IfcElementCompositionEnum.ELEMENT, - // new IFC.IfcLengthMeasure(0.0), - // new IFC.IfcLengthMeasure(0.0), - // IfcBuildingAddress - // ); - // const buildingStructure = new SpatialStructure(ifcBuilding); - // buildingStructure.Decomposed = SpatialStructure.RelAggregates( - // ifcBuilding, - // [] - // ); - // defaultLevelConfig.forEach((d) => { - // const ifcBuildingStorey = SpatialStructure.getIfcBuildingStorey(d); - // const structureLevel = new SpatialStructure(ifcBuildingStorey); - // structureLevel.Decomposed = - // SpatialStructure.RelContainedInSpatialStructure(ifcBuildingStorey); - // buildingStructure.child = structureLevel; - // this.ifcBuildingStoreys.push(structureLevel); - // }); + const ifcSite = new IFC.IfcSite( + new IFC.IfcGloballyUniqueId(uuidv4()), + this.IfcOwnerHistory, + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + IfcUtils.localPlacement(), + null, + new IFC.IfcLabel(""), + IFC.IfcElementCompositionEnum.PARTIAL, + null, + null, + null, + new IFC.IfcLabel(""), + this.IfcSiteAddress + ); + const ifcBuilding = new IFC.IfcBuilding( + new IFC.IfcGloballyUniqueId(uuidv4()), + this.IfcOwnerHistory, + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + IfcUtils.localPlacement(), + null, + new IFC.IfcLabel(""), + IFC.IfcElementCompositionEnum.ELEMENT, + new IFC.IfcLengthMeasure(0.0), + new IFC.IfcLengthMeasure(0.0), + this.IfcBuildingAddress + ); + this.buildingStructure = new SpatialStructure(ifcBuilding); + this.buildingStructure.Decomposed = this.RelAggregates(ifcBuilding, []); + defaultLevels.forEach((level: ILevel) => { + this.addLevel = level; + }); - // const siteStructure = new SpatialStructure(ifcSite); - // siteStructure.Decomposed = SpatialStructure.RelAggregates(ifcSite, []); - // siteStructure.child = buildingStructure; - // this.modelStructure = new SpatialStructure(ifcProject); - // this.modelStructure.Decomposed = SpatialStructure.RelAggregates( - // ifcProject, - // [] - // ); - // this.modelStructure.child = siteStructure; - // } + const siteStructure = new SpatialStructure(ifcSite); + siteStructure.Decomposed = this.RelAggregates(ifcSite, []); + siteStructure.child = this.buildingStructure; + this.modelStructure = new SpatialStructure(ifcProject); + this.modelStructure.Decomposed = this.RelAggregates(ifcProject, []); + this.modelStructure.child = siteStructure; + } + RelAggregates( + product: IFC.IfcObjectDefinition, + children: IFC.IfcObjectDefinition[] + ) { + return new IFC.IfcRelAggregates( + new IFC.IfcGloballyUniqueId(uuidv4()), + this.IfcOwnerHistory, + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + product, + children + ); + } + RelContainedInSpatialStructure(spatial: IFC.IfcSpatialElement) { + return new IFC.IfcRelContainedInSpatialStructure( + new IFC.IfcGloballyUniqueId(uuidv4()), + this.IfcOwnerHistory, + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + [], + spatial + ); + } + getIfcBuildingStorey(config: ILevel) { + const {name, elevation, uuid} = config; + const ifcBuildingStorey = new IFC.IfcBuildingStorey( + new IFC.IfcGloballyUniqueId(uuid), + this.IfcOwnerHistory, + new IFC.IfcLabel(name), + new IFC.IfcLabel(""), + new IFC.IfcLabel(""), + IfcUtils.localPlacement(), + null, + new IFC.IfcLabel(""), + IFC.IfcElementCompositionEnum.ELEMENT, + new IFC.IfcLengthMeasure(elevation) + ); + return ifcBuildingStorey; + } } diff --git a/src/BimModel/src/ProjectComponent/src/project/SpatialStructure.ts b/src/BimModel/src/ProjectComponent/src/project/SpatialStructure.ts index 495ebd9..caff59c 100644 --- a/src/BimModel/src/ProjectComponent/src/project/SpatialStructure.ts +++ b/src/BimModel/src/ProjectComponent/src/project/SpatialStructure.ts @@ -14,77 +14,37 @@ export type IDecomposed = | IFC.IfcRelContainedInSpatialStructure; export class SpatialStructure { - // Decomposed!: IFC.IfcRelAggregates | IFC.IfcRelContainedInSpatialStructure; - // get isDecomposed() { - // return this.Decomposed instanceof IFC.IfcRelAggregates; - // } - // get name() { - // return this.ifc.constructor.name; - // } - // get uuid() { - // return this.ifc.GlobalId?.value; - // } - // set child(child: SpatialStructure) { - // if (!this.Decomposed) return; - // if (this.children[child.uuid]) return; - // this.children[child.uuid] = child; - // const {ifc} = child; - // if (this.isDecomposed) { - // ( - // (this.Decomposed as IFC.IfcRelAggregates) - // .RelatedObjects as IFC.IfcObjectDefinition[] - // ).push(ifc); - // } else { - // ( - // (this.Decomposed as IFC.IfcRelContainedInSpatialStructure) - // .RelatedElements as IIfcProduct[] - // ).push(child.ifc); - // } - // } - // children: {[uuid: string]: SpatialStructure} = {}; - // visible = true; - // onVisibility!: (visible: boolean, spatial: SpatialStructure) => void; - // /** - // * - // */ - // constructor(public ifc: IIfcProduct) {} - // static RelAggregates( - // product: IFC.IfcObjectDefinition, - // children: IFC.IfcObjectDefinition[] - // ) { - // return new IFC.IfcRelAggregates( - // new IFC.IfcGloballyUniqueId(uuid4()), - // IfcOwnerHistory, - // new IFC.IfcLabel(""), - // new IFC.IfcLabel(""), - // product, - // children - // ); - // } - // static RelContainedInSpatialStructure(spatial: IFC.IfcSpatialElement) { - // return new IFC.IfcRelContainedInSpatialStructure( - // new IFC.IfcGloballyUniqueId(uuid4()), - // IfcOwnerHistory, - // new IFC.IfcLabel(""), - // new IFC.IfcLabel(""), - // [], - // spatial - // ); - // } - // static getIfcBuildingStorey(config: IIfcBuildingStoreyConfig) { - // const {Name, ObjectType, Description, Elevation} = config; - // const ifcBuildingStorey = new IFC.IfcBuildingStorey( - // new IFC.IfcGloballyUniqueId(uuid4()), - // IfcOwnerHistory, - // new IFC.IfcLabel(Name), - // new IFC.IfcLabel(Description), - // new IFC.IfcLabel(ObjectType), - // new IFC.IfcObjectPlacement(null), - // null, - // new IFC.IfcLabel(""), - // IFC.IfcElementCompositionEnum.ELEMENT, - // new IFC.IfcLengthMeasure(Elevation) - // ); - // return ifcBuildingStorey; - // } + Decomposed!: IFC.IfcRelAggregates | IFC.IfcRelContainedInSpatialStructure; + get isDecomposed() { + return this.Decomposed instanceof IFC.IfcRelAggregates; + } + get name() { + return this.ifc.constructor.name; + } + get uuid() { + return this.ifc.GlobalId?.value; + } + set child(child: SpatialStructure) { + if (!this.Decomposed) return; + const uuid = child.uuid; + if (!uuid) return; + if (this.children.has(uuid)) return; + this.children.set(uuid, child); + if (this.isDecomposed) { + ( + (this.Decomposed as IFC.IfcRelAggregates) + .RelatedObjects as IFC.IfcObjectDefinition[] + ).push(child.ifc); + } else { + ( + (this.Decomposed as IFC.IfcRelContainedInSpatialStructure) + .RelatedElements as IIfcProduct[] + ).push(child.ifc); + } + } + children = new Map(); + /** + * + */ + constructor(public ifc: IIfcProduct) {} } diff --git a/src/BimModel/src/ProjectComponent/src/project/index.ts b/src/BimModel/src/ProjectComponent/src/project/index.ts index 4a13849..165d68f 100644 --- a/src/BimModel/src/ProjectComponent/src/project/index.ts +++ b/src/BimModel/src/ProjectComponent/src/project/index.ts @@ -1 +1,2 @@ export * from "./ElementType"; +export * from "./IfcProject"; diff --git a/src/BimModel/src/RendererComponent/src/Camera.ts b/src/BimModel/src/RendererComponent/src/Camera.ts index b583333..219ef78 100644 --- a/src/BimModel/src/RendererComponent/src/Camera.ts +++ b/src/BimModel/src/RendererComponent/src/Camera.ts @@ -106,8 +106,6 @@ export class Camera implements Disposable, Resizeable, Updateable { } resetState() { if (!this.cameraControls) return; - const {position, target} = this.state; - this.setLookAt(position, target); this.currentCamera.far = defaultCameraFar; this.currentCamera.near = -defaultCameraFar; } diff --git a/src/BimModel/src/SelectionComponent/index.ts b/src/BimModel/src/SelectionComponent/index.ts index 92ec9d8..f7cf266 100644 --- a/src/BimModel/src/SelectionComponent/index.ts +++ b/src/BimModel/src/SelectionComponent/index.ts @@ -12,6 +12,7 @@ import {Component, Disposable, UUID} from "../types"; import {FragmentMesh} from "clay"; import {ElementLocation} from "../system"; import {changeInputSignal, selectElementSignal} from "../Signals"; +import {createContextMenu} from "./src"; /** * @@ -19,6 +20,7 @@ import {changeInputSignal, selectElementSignal} from "../Signals"; export class SelectionComponent extends Component implements Disposable { static readonly uuid = UUID.SelectionComponent; enabled = false; + private readonly hoverColor = new THREE.Color("#6528D7"); private readonly cursorTypes = [ "default", "pointer", @@ -67,11 +69,13 @@ export class SelectionComponent extends Component implements Disposable { this.container.addEventListener("mousemove", this.onMouseMove); this.container.addEventListener("mousedown", this.onMousedown); this.container.addEventListener("mouseup", this.onMouseup); + this.container.addEventListener("contextmenu", this.onContextMenu); } else { this.container.removeEventListener("click", this.onClick); this.container.removeEventListener("mousemove", this.onMouseMove); this.container.removeEventListener("mousedown", this.onMousedown); this.container.removeEventListener("mouseup", this.onMouseup); + this.container.removeEventListener("contextmenu", this.onContextMenu); } } get setupEvent() { @@ -113,10 +117,52 @@ export class SelectionComponent extends Component implements Disposable { } if (this._select) { this._select.select = true; + this.clear(); + } else { + this.showContextMenu = false; } selectElementSignal.value = this._select; } + private _hover: {[id: string]: number} = {}; + set hover(found: THREE.Intersection | null) { + if (this._select) return; + this.clear(); + if ( + !found || + !found.object || + !(found.object instanceof FragmentMesh) || + !found.object.fragment || + found.instanceId === undefined + ) + return; + const itemID = found.object.fragment.getItemID(found.instanceId); + if (!itemID) return; + const {ids} = found.object.fragment; + for (const id of ids) { + if (!this.elements[+id]) continue; + if (!this.elements[+id].element) continue; + const clones = this.elements[+id].element.type.clones; + for (const [_id, clone] of clones) { + clone.setColor(this.hoverColor, [itemID]); + } + if (!this._hover[id]) this._hover[id] = itemID; + } + } + private contextMenu!: HTMLDivElement; + private _showContextMenu = false; + set showContextMenu(show: boolean) { + this._showContextMenu = show; + if (!this.contextMenu) this.contextMenu = createContextMenu(this); + if (show) { + this.components.container.appendChild(this.contextMenu); + } else { + this.contextMenu?.remove(); + } + } + get showContextMenu() { + return this._showContextMenu; + } /** * */ @@ -126,7 +172,12 @@ export class SelectionComponent extends Component implements Disposable { this.setupEvent = true; } async dispose() { + this.enabled = false; this.setupEvent = false; + this._hover = {}; + this._select = null; + this.contextMenu?.remove(); + (this.contextMenu as any) = null; } get() { @@ -138,22 +189,44 @@ export class SelectionComponent extends Component implements Disposable { } return null; } - onClick = (_e: MouseEvent) => { + private clear() { + for (const id in this._hover) { + if (!this.elements[+id]) continue; + if (!this.elements[+id].element) continue; + const itemId = this._hover[id]; + const clones = this.elements[+id].element.type.clones; + for (const [_id, clone] of clones) { + clone.resetColor([itemId]); + } + } + this._hover = {}; + } + private onClick = (_e: MouseEvent) => { _e.preventDefault(); _e.stopPropagation(); if (this.mousedown || changeInputSignal.value) return; this.select = this.found; }; - onMouseMove = (_e: MouseEvent) => { + private onMouseMove = (_e: MouseEvent) => { if (this.mousedown) return; this.find = _e; + // this.hover = this.found; }; - onMousedown = (_e: MouseEvent) => { + private onMousedown = (_e: MouseEvent) => { if (_e.button === 0) this.mousedown = true; }; private onMouseup = () => { this.mousedown = false; }; + private onContextMenu = (_e: MouseEvent) => { + _e.preventDefault(); + _e.stopPropagation(); + this.showContextMenu = this._select !== null && this._select !== undefined; + const {clientX, clientY} = _e; + const bounds = this.components.rect; + this.contextMenu.style.top = `${clientY - bounds.top}px`; + this.contextMenu.style.left = `${clientX - bounds.left + 10}px`; + }; } // ToolComponent.libraryUUIDs.add(SelectionComponent.uuid); diff --git a/src/BimModel/src/SelectionComponent/src/ContextMenu/ContextMenuPanel.tsx b/src/BimModel/src/SelectionComponent/src/ContextMenu/ContextMenuPanel.tsx new file mode 100644 index 0000000..055e3df --- /dev/null +++ b/src/BimModel/src/SelectionComponent/src/ContextMenu/ContextMenuPanel.tsx @@ -0,0 +1,34 @@ +import React, {useEffect, useState} from "react"; +import {SelectionComponent} from "../.."; + +const contextMenuBg = "bg-slate-800"; +const ContextMenuPanel = ({ + selectionComponent, +}: { + selectionComponent: SelectionComponent; +}) => { + useEffect(() => { + console.log(selectionComponent); + }, []); + return ( +
+ + + +
+ ); +}; + +export default ContextMenuPanel; diff --git a/src/BimModel/src/SelectionComponent/src/ContextMenu/index.tsx b/src/BimModel/src/SelectionComponent/src/ContextMenu/index.tsx new file mode 100644 index 0000000..8b63ae9 --- /dev/null +++ b/src/BimModel/src/SelectionComponent/src/ContextMenu/index.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import ContextMenuPanel from "./ContextMenuPanel"; +import {SelectionComponent} from "../.."; + +export function createContextMenu( + selectionComponent: SelectionComponent +): HTMLDivElement { + const div = document.createElement("div"); + div.className = "absolute z-4000"; + ReactDOM.createRoot(div!).render( + + ); + return div; +} diff --git a/src/BimModel/src/SelectionComponent/src/index.ts b/src/BimModel/src/SelectionComponent/src/index.ts new file mode 100644 index 0000000..24a35cf --- /dev/null +++ b/src/BimModel/src/SelectionComponent/src/index.ts @@ -0,0 +1 @@ +export * from "./ContextMenu"; diff --git a/src/BimModel/src/SelectionComponent/types.ts b/src/BimModel/src/SelectionComponent/types.ts new file mode 100644 index 0000000..da3c00e --- /dev/null +++ b/src/BimModel/src/SelectionComponent/types.ts @@ -0,0 +1,3 @@ +export interface IContextMenu { + active: boolean; +} diff --git a/src/BimModel/src/Signals/ElementType/index.ts b/src/BimModel/src/Signals/ElementType/index.ts index 4105857..7058d73 100644 --- a/src/BimModel/src/Signals/ElementType/index.ts +++ b/src/BimModel/src/Signals/ElementType/index.ts @@ -1,10 +1,18 @@ import {ElementLocation} from "@BimModel/src/system"; -import {signal} from "@preact/signals-react"; +import {effect, signal} from "@preact/signals-react"; +import {disciplineSignal} from "../Modeling"; export const openElementTypeSignal = signal(false); export const tempElementSignal = signal(null); export const selectElementSignal = signal(null); +/** + * + */ +effect(() => { + if (selectElementSignal.value) disciplineSignal.value = "Modify"; +}); + export function disposeElementType() { openElementTypeSignal.value = false; tempElementSignal.value = null; diff --git a/src/BimModel/src/Signals/Modeling/index.ts b/src/BimModel/src/Signals/Modeling/index.ts index 27798c3..1b1840f 100644 --- a/src/BimModel/src/Signals/Modeling/index.ts +++ b/src/BimModel/src/Signals/Modeling/index.ts @@ -1,7 +1,17 @@ -import {IDrawType, IModelingTool} from "@ModelingComponent/types"; +import { + IDrawType, + IModelingTab, + IModelingTool, + IModify, +} from "@ModelingComponent/types"; import {effect, signal} from "@preact/signals-react"; -export const disciplineSignal = signal("Files"); +export const fileTab: IModelingTab = "Files"; +export const modifyTab: IModelingTab = "Modify"; +/** + * + */ +export const disciplineSignal = signal(fileTab); export const modelingSignal = signal(null); export const drawingTypeSignal = signal("None"); @@ -11,6 +21,7 @@ export const changeInputSignal = signal(false); //#region export const isModelingSignal = signal(false); export const isOrthoSignal = signal(true); +export const modifySignal = signal(null); //#endregion effect(() => { @@ -21,10 +32,11 @@ effect(() => { }); export function disposeModeling() { - disciplineSignal.value = "Files"; + disciplineSignal.value = fileTab; modelingSignal.value = null; drawingTypeSignal.value = "None"; isModelingSignal.value = false; isOrthoSignal.value = false; changeInputSignal.value = false; + modifySignal.value = null; } diff --git a/src/BimModel/src/Signals/Project/index.ts b/src/BimModel/src/Signals/Project/index.ts index 4aecc07..ccbd3ad 100644 --- a/src/BimModel/src/Signals/Project/index.ts +++ b/src/BimModel/src/Signals/Project/index.ts @@ -4,8 +4,10 @@ export * from "./line"; export const projectSignal = signal(null); export const openProjectInfoSignal = signal(false); export const newProjectInfoSignal = signal(false); +export const exportIfcSignal = signal(false); export function disposeProject() { projectSignal.value = null; openProjectInfoSignal.value = false; newProjectInfoSignal.value = false; + exportIfcSignal.value = false; } diff --git a/src/BimModel/src/system/element/ElementLocation.ts b/src/BimModel/src/system/element/ElementLocation.ts index 12c6c2f..1a10dce 100644 --- a/src/BimModel/src/system/element/ElementLocation.ts +++ b/src/BimModel/src/system/element/ElementLocation.ts @@ -1,19 +1,35 @@ -import {IElement, IElementType} from "clay"; +import * as THREE from "three"; +import {IFC4X3 as IFC} from "web-ifc"; + +import {IElement, IElementType, Model} from "clay"; import {LocationArc, LocationLine, LocationPoint} from "../geometry"; -import {BaseParameterGroup} from "./Parameter"; +import {BaseParameterGroup, LevelParameter} from "./Parameter"; import {IBimElementType} from "@ProjectComponent/types"; import {ICategory} from "./types"; import {QsetWallCommon} from "./Parameter/wall/QsetWallCommon"; import {QsetBeamBaseQuantity} from "./Parameter/beam"; import {IAttribute} from "@BimModel/src/system"; -import {Disposable} from "@BimModel/src"; -import {QsetColumnBaseQuantity} from "./Parameter/Column"; +import { + Disposable, + Components, + ProjectComponent, + MaterialComponent, +} from "@BimModel/src"; +import {QsetColumnBaseQuantity} from "./Parameter/column"; export class ElementLocation implements Disposable { groupParameter: {[uuid: string]: BaseParameterGroup} = {}; location!: LocationPoint | LocationArc | LocationLine; element!: IElement; - + get ProjectComponent() { + return this.components.tools.get(ProjectComponent); + } + get materialCategories() { + return this.components.tools.get(MaterialComponent)?.materialCategories; + } + get elements() { + return this.ProjectComponent?.elements; + } get attributes(): IAttribute | undefined { if (!this.element) return undefined; return { @@ -25,14 +41,25 @@ export class ElementLocation implements Disposable { set select(select: boolean) { if (this._select === select) return; this._select = select; - if (this.location) this.location.visible = select; + if (this.location) { + this.location.visible = select; + } + } + getLevelParameter(): LevelParameter | null { + if (Object.keys(this.groupParameter).length === 0) return null; + for (const key in this.groupParameter) { + const group = this.groupParameter[key]; + const level = group.getLevelParameter(); + if (level) return level; + } + return null; } - /** * */ constructor( public category: ICategory, + public components: Components, public bimElementTypes: IBimElementType ) {} async dispose() { @@ -46,8 +73,51 @@ export class ElementLocation implements Disposable { this.location.updateLength(length); if (this.element.updateLocation) this.element.updateLocation(this.location.location); + this.components.modelScene.add(...this.element.clones); this.updateElement(); }; + onMove = (origin: THREE.Vector3, movingPoint: THREE.Vector3) => { + if (!this.element || !this.location) return; + if (this.location instanceof LocationLine) { + this.location.updateMove(origin, movingPoint); + } else if (this.location instanceof LocationArc) { + // + } else if (this.location instanceof LocationPoint) { + this.location.update(movingPoint); + } + + if (this.element.updateLocation) + this.element.updateLocation(this.location.location); + this.components.modelScene.add(...this.element.clones); + this.updateElement(); + }; + onCopy = (origin: THREE.Vector3, movingPoint: THREE.Vector3) => { + if (!this.element || !this.location) return; + const material = this.materialCategories[this.category]; + const currentElementIndex = Object.keys( + this.ProjectComponent.elements + ).length; + if (this.location instanceof LocationLine) { + const location = this.location.onClone(); + location.updateMove(origin, movingPoint); + const element = this.element.onClone(material!) as IElement; + element.updateDraw(location.location); + element.attributes.Name = new IFC.IfcLabel( + `${this.category} ${currentElementIndex + 1}` + ); + const elementLocation = this.ProjectComponent.setElement( + this.category, + this.bimElementTypes, + element, + location + ); + elementLocation.clonePset(); + } else if (this.location instanceof LocationArc) { + // + } else if (this.location instanceof LocationPoint) { + // + } + }; addQsetWallCommon() { const qset = new QsetWallCommon(); @@ -64,10 +134,37 @@ export class ElementLocation implements Disposable { this.groupParameter[qset.uuid] = qset; this.updateElement(); } + export() { + if (!this.element) return; + const IfcOwnerHistory = this.components.ifcModel.IfcOwnerHistory; + this.element.attributes.OwnerHistory = IfcOwnerHistory; + for (const uuid in this.groupParameter) { + this.groupParameter[uuid].export( + this.components.ifcModel, + IfcOwnerHistory + ); + } + } private updateElement() { if (!this.element) return; for (const uuid in this.groupParameter) { this.groupParameter[uuid].updateElement(this.element); } } + private clonePset() { + switch (this.category) { + case "Structure Beam": + this.addQsetBeamCommon(); + break; + case "Wall": + case "Structure Wall": + this.addQsetWallCommon(); + break; + case "Structure Column": + this.addQsetColumnCommon(); + break; + default: + break; + } + } } diff --git a/src/BimModel/src/system/element/ElementUtils.ts b/src/BimModel/src/system/element/ElementUtils.ts index 093aa45..88ab7ce 100644 --- a/src/BimModel/src/system/element/ElementUtils.ts +++ b/src/BimModel/src/system/element/ElementUtils.ts @@ -1,6 +1,7 @@ import { ArbitraryClosedProfile, IIfcBaseConfig, + IShapeProfile, Model, RectangleProfile, } from "clay"; @@ -18,82 +19,112 @@ import {ElementLocation} from "./ElementLocation"; import {PsetWallLevelCommon} from "./Parameter"; import {currentLevelSignal} from "@BimModel/src/Signals"; import {PsetBeamLevelCommon} from "./Parameter/beam"; -import {PsetColumnLevelCommon} from "./Parameter/Column"; +import {PsetColumnLevelCommon} from "./Parameter/column"; +import {Components} from "@BimModel/src/Components"; export class ElementUtils { - static createWallInstance(category: ICategory, model: Model) { + static createWallInstance( + category: ICategory, + components: Components, + model: Model + ) { const types = WallTypeUtils.getDefaultWallTypes(model); - const wall = new ElementLocation(category, types); + const wall = new ElementLocation(category, components, types); const pset = new PsetWallLevelCommon(currentLevelSignal.value!); wall.groupParameter[pset.uuid] = pset; return wall; } - static createSlabInstance(category: ICategory, model: Model) { + static createSlabInstance( + category: ICategory, + components: Components, + model: Model + ) { const slabs = SlabTypeUtils.getDefaultSlabTypes(model); - return new ElementLocation(category, slabs); + return new ElementLocation(category, components, slabs); } - static createWindowInstance(category: ICategory, model: Model) { + static createWindowInstance( + category: ICategory, + components: Components, + model: Model + ) { const windows = WindowTypeUtils.getDefaultWindowTypes(model); - return new ElementLocation(category, windows); + return new ElementLocation(category, components, windows); } - static createCurtainWallInstance(category: ICategory, model: Model) { + static createCurtainWallInstance( + category: ICategory, + components: Components, + model: Model + ) { const curtainWalls = CurtainWallTypeUtils.getDefaultCurtainWallTypes(model); - return new ElementLocation(category, curtainWalls); + return new ElementLocation(category, components, curtainWalls); } static createColumnInstance( category: ICategory, + components: Components, model: Model, configs: { config: IIfcBaseConfig; - profile: RectangleProfile | ArbitraryClosedProfile; + profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; }[] ) { const columns = ColumnTypeUtils.getDefaultColumnTypes(model, configs); - const column = new ElementLocation(category, columns); + const column = new ElementLocation(category, components, columns); const pset = new PsetColumnLevelCommon(currentLevelSignal.value!); column.groupParameter[pset.uuid] = pset; return column; } static createBeamInstance( category: ICategory, + components: Components, model: Model, configs: { config: IIfcBaseConfig; - profile: RectangleProfile | ArbitraryClosedProfile; + profile: RectangleProfile | ArbitraryClosedProfile | IShapeProfile; }[] ) { const types = BeamTypeUtils.getDefaultBeamTypes(model, configs); - const beam = new ElementLocation(category, types); + const beam = new ElementLocation(category, components, types); const pset = new PsetBeamLevelCommon(currentLevelSignal.value!); beam.groupParameter[pset.uuid] = pset; return beam; } static createTempElementInstances( - model: Model + components: Components ): Record { + const model = components.ifcModel; const profiles = ProfileUtils.createProfiles(model); return { - Wall: this.createWallInstance("Wall", model), - Floor: this.createSlabInstance("Floor", model), + Wall: this.createWallInstance("Wall", components, model), + Floor: this.createSlabInstance("Floor", components, model), Ceiling: null, Roof: null, - Column: this.createColumnInstance("Column", model, profiles), + Column: this.createColumnInstance("Column", components, model, profiles), Door: null, - Window: this.createWindowInstance("Window", model), - CurtainWall: this.createCurtainWallInstance("CurtainWall", model), + Window: this.createWindowInstance("Window", components, model), + CurtainWall: null, "Structure Beam": this.createBeamInstance( "Structure Beam", + components, model, profiles ), "Structure Column": this.createColumnInstance( "Structure Column", + components, model, profiles ), - "Structure Wall": this.createWallInstance("Structure Wall", model), - "Structure Slab": this.createSlabInstance("Structure Slab", model), + "Structure Wall": this.createWallInstance( + "Structure Wall", + components, + model + ), + "Structure Slab": this.createSlabInstance( + "Structure Slab", + components, + model + ), "Structure Foundation": null, ReinForcement: null, Duct: null, diff --git a/src/BimModel/src/system/element/Parameter/AreaParameter.ts b/src/BimModel/src/system/element/Parameter/AreaParameter.ts index 188f7f5..82552a7 100644 --- a/src/BimModel/src/system/element/Parameter/AreaParameter.ts +++ b/src/BimModel/src/system/element/Parameter/AreaParameter.ts @@ -21,7 +21,15 @@ export class AreaParameter extends BaseParameter { super(); this.name = name; } - toIfc!: () => IFC.IfcQuantityArea; + toIfc = () => { + return new IFC.IfcQuantityArea( + new IFC.IfcIdentifier(this.name || ""), + null, + null, + new IFC.IfcLengthMeasure(this.value), + null + ); + }; onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; diff --git a/src/BimModel/src/system/element/Parameter/BaseParameter.ts b/src/BimModel/src/system/element/Parameter/BaseParameter.ts index a7e0f3c..02b52a7 100644 --- a/src/BimModel/src/system/element/Parameter/BaseParameter.ts +++ b/src/BimModel/src/system/element/Parameter/BaseParameter.ts @@ -29,6 +29,7 @@ export abstract class BaseParameter { | IFC.IfcQuantityArea | IFC.IfcQuantityVolume | IFC.IfcQuantityWeight - | IFC.IfcPropertyReferenceValue; + | IFC.IfcPropertyReferenceValue + | IFC.IfcProperty; enable = true; } diff --git a/src/BimModel/src/system/element/Parameter/BaseParameterGroup.ts b/src/BimModel/src/system/element/Parameter/BaseParameterGroup.ts index 655368d..96cf21e 100644 --- a/src/BimModel/src/system/element/Parameter/BaseParameterGroup.ts +++ b/src/BimModel/src/system/element/Parameter/BaseParameterGroup.ts @@ -1,17 +1,81 @@ -import {IElement} from "clay"; -import {BaseParameter} from "./BaseParameter"; +import {IElement, Model} from "clay"; +import {v4 as uuid4} from "uuid"; import {IFC4X3 as IFC} from "web-ifc"; + +import {BaseParameter} from "./BaseParameter"; +import {LevelParameter} from "./LevelParameter"; +import {LengthParameter} from "./LengthParameter"; +import {AreaParameter} from "./AreaParameter"; +import {VolumeParameter} from "./VolumeParameter"; +import {WeightParameter} from "./WeightParameter"; /** * */ export abstract class BaseParameterGroup { - abstract uuid: string; + uuid = uuid4(); abstract name: string; abstract HasProperties: {[uuid: string]: BaseParameter}; abstract element: IElement; - abstract toIfc: () => IFC.IfcPropertySet; + toIfc = (IfcOwnerHistory: IFC.IfcOwnerHistory): IFC.IfcPropertySet => { + if (!this.element) throw new Error("Can not find element"); + if (!this.name) throw new Error("Name has no assignment"); + return new IFC.IfcPropertySet( + new IFC.IfcGloballyUniqueId(this.uuid), + IfcOwnerHistory, + new IFC.IfcLabel(this.name), + null, + [] + ); + }; + abstract updateElement: (element: IElement) => void; - /** - * - */ + + getLevelParameter(): LevelParameter | null { + if (Object.keys(this.HasProperties).length === 0) return null; + for (const key in this.HasProperties) { + const para = this.HasProperties[key]; + if (para instanceof LevelParameter) return para; + } + return null; + } + export(model: Model, IfcOwnerHistory: IFC.IfcOwnerHistory) { + if (!this.element) return; + if (!this.HasProperties || Object.keys(this.HasProperties).length === 0) + return; + const pSet = this.toIfc(IfcOwnerHistory); + if (!pSet) return; + pSet.OwnerHistory = IfcOwnerHistory; + pSet.HasProperties = []; + + const {ifcUnit} = model.ifcInfo; + const {IfcAreaUnit, IfcLengthUnit, IfcVolumeUnit, IfcMassUnit} = ifcUnit; + for (const key in this.HasProperties) { + const para = this.HasProperties[key]; + const ifc = para.toIfc(); + if (!ifc) continue; + if (para instanceof LengthParameter) { + (ifc as IFC.IfcQuantityLength).Unit = IfcLengthUnit; + } + if (para instanceof AreaParameter) { + (ifc as IFC.IfcQuantityArea).Unit = IfcAreaUnit; + } + if (para instanceof VolumeParameter) { + (ifc as IFC.IfcQuantityVolume).Unit = IfcVolumeUnit; + } + if (para instanceof WeightParameter) { + (ifc as IFC.IfcQuantityWeight).Unit = IfcMassUnit; + } + pSet.HasProperties.push(ifc as IFC.IfcProperty); + } + const relPset = new IFC.IfcRelDefinesByProperties( + new IFC.IfcGloballyUniqueId(uuid4()), + IfcOwnerHistory, + null, + null, + [this.element.attributes], + pSet + ); + model.set(pSet); + model.set(relPset); + } } diff --git a/src/BimModel/src/system/element/Parameter/Column/PsetColumnLevelCommon.ts b/src/BimModel/src/system/element/Parameter/Column/PsetColumnLevelCommon.ts index 72b1d02..9f5780c 100644 --- a/src/BimModel/src/system/element/Parameter/Column/PsetColumnLevelCommon.ts +++ b/src/BimModel/src/system/element/Parameter/Column/PsetColumnLevelCommon.ts @@ -1,9 +1,7 @@ -import {v4 as uuid4} from "uuid"; import {BaseParameter} from "../BaseParameter"; import {BaseParameterGroup} from "../BaseParameterGroup"; import {IElement} from "clay"; import {LevelParameter} from "../LevelParameter"; -import {IFC4X3 as IFC} from "web-ifc"; import {BottomLevelParameter, TopLevelParameter} from "../OffsetLevelParameter"; import {ILevel} from "@BimModel/src/LevelSystem/types"; @@ -12,7 +10,7 @@ import {ILevel} from "@BimModel/src/LevelSystem/types"; */ export class PsetColumnLevelCommon extends BaseParameterGroup { element!: IElement; - uuid = uuid4(); + name = "Column Level Common" as const; HasProperties: {[uuid: string]: BaseParameter} = {}; levelParameter!: LevelParameter; @@ -54,5 +52,4 @@ export class PsetColumnLevelCommon extends BaseParameterGroup { if (!this.element) return; // const {} = value as ILevel; }; - toIfc!: () => IFC.IfcPropertySet; } diff --git a/src/BimModel/src/system/element/Parameter/Column/QsetColumnBaseQuantity.ts b/src/BimModel/src/system/element/Parameter/Column/QsetColumnBaseQuantity.ts index b8ae775..26bd83d 100644 --- a/src/BimModel/src/system/element/Parameter/Column/QsetColumnBaseQuantity.ts +++ b/src/BimModel/src/system/element/Parameter/Column/QsetColumnBaseQuantity.ts @@ -1,8 +1,6 @@ -import {v4 as uuid4} from "uuid"; import {BaseParameter} from "../BaseParameter"; import {BaseParameterGroup} from "../BaseParameterGroup"; import {IElement, SimpleWall, SimpleWallType} from "clay"; -import {IFC4X3 as IFC} from "web-ifc"; import {LengthParameter} from "../LengthParameter"; import {AreaParameter} from "../AreaParameter"; import {VolumeParameter} from "../VolumeParameter"; @@ -12,7 +10,6 @@ import {VolumeParameter} from "../VolumeParameter"; */ export class QsetColumnBaseQuantity extends BaseParameterGroup { element!: IElement; - uuid = uuid4(); name = "Qto_ColumnBaseQuantities" as const; HasProperties: {[uuid: string]: BaseParameter} = {}; Length!: LengthParameter; @@ -50,7 +47,6 @@ export class QsetColumnBaseQuantity extends BaseParameterGroup { this.disabled(); } - toIfc!: () => IFC.IfcPropertySet; private disabled() { for (const key in this.HasProperties) { this.HasProperties[key].enable = false; @@ -59,14 +55,6 @@ export class QsetColumnBaseQuantity extends BaseParameterGroup { updateElement = (element: IElement) => { this.element = element; if (!this.element.type) return; - const {height, length} = this.element as SimpleWall; - this.Length.value = length; - this.Width.value = (this.element.type as SimpleWallType).width; - this.Height.value = height; - this.GrossFootPrintArea.value = this.Width.value * this.Height.value; - this.NetFootPrintArea.value = this.Width.value * this.Height.value; - this.GrossSideArea.value = this.Length.value * this.Height.value; - this.NetSideArea.value = this.Length.value * this.Height.value; }; onChangeLength = (value: number) => { if (!this.element) return; diff --git a/src/BimModel/src/system/element/Parameter/LengthParameter.ts b/src/BimModel/src/system/element/Parameter/LengthParameter.ts index 93ce5d7..76c0200 100644 --- a/src/BimModel/src/system/element/Parameter/LengthParameter.ts +++ b/src/BimModel/src/system/element/Parameter/LengthParameter.ts @@ -21,7 +21,16 @@ export class LengthParameter extends BaseParameter { super(); this.name = name; } - toIfc!: () => IFC.IfcQuantityLength; + + toIfc = () => { + return new IFC.IfcQuantityLength( + new IFC.IfcIdentifier(this.name || ""), + null, + null, + new IFC.IfcLengthMeasure(this.value), + null + ); + }; onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; diff --git a/src/BimModel/src/system/element/Parameter/LevelParameter.ts b/src/BimModel/src/system/element/Parameter/LevelParameter.ts index f9d5caf..683b805 100644 --- a/src/BimModel/src/system/element/Parameter/LevelParameter.ts +++ b/src/BimModel/src/system/element/Parameter/LevelParameter.ts @@ -22,7 +22,15 @@ export class LevelParameter extends BaseParameter { super(); this.value = level; } - toIfc!: () => IFC.IfcPropertySingleValue | IFC.IfcPropertyReferenceValue; + toIfc = () => { + return new IFC.IfcPropertySingleValue( + new IFC.IfcIdentifier(this.name), + null, + new IFC.IfcLabel(this.value?.name), + null + ); + }; + onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; diff --git a/src/BimModel/src/system/element/Parameter/OffsetLevelParameter.ts b/src/BimModel/src/system/element/Parameter/OffsetLevelParameter.ts index 8de3a87..aa68839 100644 --- a/src/BimModel/src/system/element/Parameter/OffsetLevelParameter.ts +++ b/src/BimModel/src/system/element/Parameter/OffsetLevelParameter.ts @@ -20,7 +20,14 @@ export class TopLevelParameter extends BaseParameter { constructor(public level: ILevel) { super(); } - toIfc!: () => IFC.IfcPropertySingleValue | IFC.IfcPropertyReferenceValue; + toIfc = () => { + return new IFC.IfcPropertySingleValue( + new IFC.IfcIdentifier(this.name), + null, + new IFC.IfcLengthMeasure(this.value), + null + ); + }; onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; @@ -43,7 +50,14 @@ export class BottomLevelParameter extends BaseParameter { constructor(public level: ILevel) { super(); } - toIfc!: () => IFC.IfcPropertySingleValue | IFC.IfcPropertyReferenceValue; + toIfc = () => { + return new IFC.IfcPropertySingleValue( + new IFC.IfcIdentifier(this.name), + null, + new IFC.IfcLengthMeasure(this.value), + null + ); + }; onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; diff --git a/src/BimModel/src/system/element/Parameter/VolumeParameter.ts b/src/BimModel/src/system/element/Parameter/VolumeParameter.ts index 9977c80..1de9c2b 100644 --- a/src/BimModel/src/system/element/Parameter/VolumeParameter.ts +++ b/src/BimModel/src/system/element/Parameter/VolumeParameter.ts @@ -21,7 +21,15 @@ export class VolumeParameter extends BaseParameter { super(); this.name = name; } - toIfc!: () => IFC.IfcQuantityVolume; + toIfc = () => { + return new IFC.IfcQuantityVolume( + new IFC.IfcIdentifier(this.name || ""), + null, + null, + new IFC.IfcLengthMeasure(this.value), + null + ); + }; onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; diff --git a/src/BimModel/src/system/element/Parameter/WeightParameter.ts b/src/BimModel/src/system/element/Parameter/WeightParameter.ts index f79142a..1428e58 100644 --- a/src/BimModel/src/system/element/Parameter/WeightParameter.ts +++ b/src/BimModel/src/system/element/Parameter/WeightParameter.ts @@ -21,7 +21,15 @@ export class WeightParameter extends BaseParameter { super(); this.name = name; } - toIfc!: () => IFC.IfcQuantityWeight; + toIfc = () => { + return new IFC.IfcQuantityWeight( + new IFC.IfcIdentifier(this.name || ""), + null, + null, + new IFC.IfcLengthMeasure(this.value), + null + ); + }; onValueChange!: ( value: string | number | boolean | ILevel | BaseParameter ) => void; diff --git a/src/BimModel/src/system/element/Parameter/beam/PsetBeamLevelCommon.ts b/src/BimModel/src/system/element/Parameter/beam/PsetBeamLevelCommon.ts index 2d4add5..7286f6e 100644 --- a/src/BimModel/src/system/element/Parameter/beam/PsetBeamLevelCommon.ts +++ b/src/BimModel/src/system/element/Parameter/beam/PsetBeamLevelCommon.ts @@ -1,4 +1,3 @@ -import {v4 as uuid4} from "uuid"; import {BaseParameter} from "../BaseParameter"; import {BaseParameterGroup} from "../BaseParameterGroup"; import {IElement} from "clay"; @@ -12,7 +11,6 @@ import {ILevel} from "@BimModel/src/LevelSystem/types"; */ export class PsetBeamLevelCommon extends BaseParameterGroup { element!: IElement; - uuid = uuid4(); name = "Beam Level Common" as const; HasProperties: {[uuid: string]: BaseParameter} = {}; levelParameter!: LevelParameter; @@ -42,6 +40,4 @@ export class PsetBeamLevelCommon extends BaseParameterGroup { if (!this.element) return; // const {} = value as ILevel; }; - - toIfc!: () => IFC.IfcPropertySet; } diff --git a/src/BimModel/src/system/element/Parameter/beam/QsetBeamBaseQuantity.ts b/src/BimModel/src/system/element/Parameter/beam/QsetBeamBaseQuantity.ts index b1284ba..6e9b0f9 100644 --- a/src/BimModel/src/system/element/Parameter/beam/QsetBeamBaseQuantity.ts +++ b/src/BimModel/src/system/element/Parameter/beam/QsetBeamBaseQuantity.ts @@ -1,8 +1,12 @@ -import {v4 as uuid4} from "uuid"; import {BaseParameter} from "../BaseParameter"; import {BaseParameterGroup} from "../BaseParameterGroup"; -import {IElement, RectangleProfile, SimpleBeam, SimpleBeamType} from "clay"; -import {IFC4X3 as IFC} from "web-ifc"; +import { + IElement, + IShapeProfile, + RectangleProfile, + SimpleBeam, + SimpleBeamType, +} from "clay"; import {LengthParameter} from "../LengthParameter"; import {AreaParameter} from "../AreaParameter"; import {VolumeParameter} from "../VolumeParameter"; @@ -13,7 +17,6 @@ import {WeightParameter} from "../WeightParameter"; */ export class QsetBeamBaseQuantity extends BaseParameterGroup { element!: IElement; - uuid = uuid4(); name = "QsetBeamBaseQuantity" as const; HasProperties: {[uuid: string]: BaseParameter} = {}; Length!: LengthParameter; @@ -45,7 +48,6 @@ export class QsetBeamBaseQuantity extends BaseParameterGroup { this.disabled(); } - toIfc!: () => IFC.IfcPropertySet; private disabled() { for (const key in this.HasProperties) { this.HasProperties[key].enable = false; @@ -55,13 +57,17 @@ export class QsetBeamBaseQuantity extends BaseParameterGroup { this.element = element; if (!this.element.type) return; const {length} = this.element as SimpleBeam; - const {x, y} = ( - (this.element.type as SimpleBeamType).profile as RectangleProfile - ).dimension; this.Length.value = length; - this.CrossSectionArea.value = x * y; - this.OuterSurfaceArea.value = x * y; - this.GrossVolume.value = x * y * length; - this.NetVolume.value = x * y * length; + let area = 0; + const profile = (this.element.type as SimpleBeamType).profile; + if (profile instanceof RectangleProfile) { + area = profile.dimension.x * profile.dimension.y; + } else if (profile instanceof IShapeProfile) { + area = profile.width * profile.height; + } + this.CrossSectionArea.value = area; + this.OuterSurfaceArea.value = area; + this.GrossVolume.value = area * length; + this.NetVolume.value = area * length; }; } diff --git a/src/BimModel/src/system/element/Parameter/index.ts b/src/BimModel/src/system/element/Parameter/index.ts index 854926b..97faf48 100644 --- a/src/BimModel/src/system/element/Parameter/index.ts +++ b/src/BimModel/src/system/element/Parameter/index.ts @@ -1,3 +1,6 @@ export * from "./BaseParameter"; export * from "./BaseParameterGroup"; +export * from "./LevelParameter"; export * from "./wall"; +export * from "./beam"; +export * from "./column"; diff --git a/src/BimModel/src/system/element/Parameter/wall/PsetWallLevelCommon.ts b/src/BimModel/src/system/element/Parameter/wall/PsetWallLevelCommon.ts index 09f46dd..4da808d 100644 --- a/src/BimModel/src/system/element/Parameter/wall/PsetWallLevelCommon.ts +++ b/src/BimModel/src/system/element/Parameter/wall/PsetWallLevelCommon.ts @@ -1,9 +1,7 @@ -import {v4 as uuid4} from "uuid"; import {BaseParameter} from "../BaseParameter"; import {BaseParameterGroup} from "../BaseParameterGroup"; import {IElement} from "clay"; import {LevelParameter} from "../LevelParameter"; -import {IFC4X3 as IFC} from "web-ifc"; import {BottomLevelParameter, TopLevelParameter} from "../OffsetLevelParameter"; import {ILevel} from "@BimModel/src/LevelSystem/types"; @@ -12,7 +10,6 @@ import {ILevel} from "@BimModel/src/LevelSystem/types"; */ export class PsetWallLevelCommon extends BaseParameterGroup { element!: IElement; - uuid = uuid4(); name = "Wall Level Common" as const; HasProperties: {[uuid: string]: BaseParameter} = {}; levelParameter!: LevelParameter; @@ -54,5 +51,4 @@ export class PsetWallLevelCommon extends BaseParameterGroup { if (!this.element) return; // const {} = value as ILevel; }; - toIfc!: () => IFC.IfcPropertySet; } diff --git a/src/BimModel/src/system/element/Parameter/wall/QsetWallCommon.ts b/src/BimModel/src/system/element/Parameter/wall/QsetWallCommon.ts index eb951dd..a0a178c 100644 --- a/src/BimModel/src/system/element/Parameter/wall/QsetWallCommon.ts +++ b/src/BimModel/src/system/element/Parameter/wall/QsetWallCommon.ts @@ -1,8 +1,6 @@ -import {v4 as uuid4} from "uuid"; import {BaseParameter} from "../BaseParameter"; import {BaseParameterGroup} from "../BaseParameterGroup"; import {IElement, SimpleWall, SimpleWallType} from "clay"; -import {IFC4X3 as IFC} from "web-ifc"; import {LengthParameter} from "../LengthParameter"; import {AreaParameter} from "../AreaParameter"; import {VolumeParameter} from "../VolumeParameter"; @@ -12,7 +10,6 @@ import {VolumeParameter} from "../VolumeParameter"; */ export class QsetWallCommon extends BaseParameterGroup { element!: IElement; - uuid = uuid4(); name = "Qto_WallBaseQuantities" as const; HasProperties: {[uuid: string]: BaseParameter} = {}; Length!: LengthParameter; @@ -50,7 +47,6 @@ export class QsetWallCommon extends BaseParameterGroup { this.disabled(); } - toIfc!: () => IFC.IfcPropertySet; private disabled() { for (const key in this.HasProperties) { this.HasProperties[key].enable = false; diff --git a/src/BimModel/src/system/geometry/index.ts b/src/BimModel/src/system/geometry/index.ts index 4bbb5f2..0e0ccb6 100644 --- a/src/BimModel/src/system/geometry/index.ts +++ b/src/BimModel/src/system/geometry/index.ts @@ -1,3 +1,4 @@ export * from "./types"; export * from "./location"; export * from "./shape"; +export * from "./moving"; diff --git a/src/BimModel/src/system/geometry/location/BaseLocation.ts b/src/BimModel/src/system/geometry/location/BaseLocation.ts index 14fd166..4270f05 100644 --- a/src/BimModel/src/system/geometry/location/BaseLocation.ts +++ b/src/BimModel/src/system/geometry/location/BaseLocation.ts @@ -1,6 +1,7 @@ /** * @module GeometrySystem */ +import * as THREE from "three"; import {Components, MaterialComponent} from "@BimModel/src"; @@ -21,9 +22,13 @@ export abstract class BaseLocation { this._visible = visible; if (this.onVisibility) this.onVisibility(visible); } + get visible() { + return this._visible; + } abstract onSelect: (select: boolean) => void; abstract onHover: (hover: boolean) => void; abstract onVisibility: (visible: boolean) => void; + abstract onClone: () => BaseLocation; get LocationMaterial() { return this.components.tools.get(MaterialComponent)?.LocationMaterial; } @@ -36,5 +41,5 @@ export abstract class BaseLocation { /** * */ - constructor(public components: Components) {} + constructor(public components: Components, public workPlane: THREE.Plane) {} } diff --git a/src/BimModel/src/system/geometry/location/Components/DimensionLocationLine.tsx b/src/BimModel/src/system/geometry/location/Components/DimensionLocationLine.tsx index 08e580e..6c232a2 100644 --- a/src/BimModel/src/system/geometry/location/Components/DimensionLocationLine.tsx +++ b/src/BimModel/src/system/geometry/location/Components/DimensionLocationLine.tsx @@ -7,6 +7,8 @@ import {LocationLine} from "../LocationLine"; const DimensionLocationLine = ({location}: {location: LocationLine}) => { const inputRef = useRef(null); useEffect(() => { + const {factor, toFixed} = lengthUnitSignal.value; + inputRef.current!.value = `${(location.length * factor).toFixed(toFixed)}`; location.onChangeLengthDomElement = (length: string) => { inputRef.current!.value = length; }; diff --git a/src/BimModel/src/system/geometry/location/LocationArc.ts b/src/BimModel/src/system/geometry/location/LocationArc.ts index ab5a1d8..92f7a23 100644 --- a/src/BimModel/src/system/geometry/location/LocationArc.ts +++ b/src/BimModel/src/system/geometry/location/LocationArc.ts @@ -85,8 +85,8 @@ export class LocationArc /** * */ - constructor(components: Components, private workPlane: THREE.Plane) { - super(components); + constructor(components: Components, workPlane: THREE.Plane) { + super(components, workPlane); this.init(); } /** @@ -332,4 +332,7 @@ export class LocationArc onChangeAngle!: (angle: number) => void; onChangeAngleDomElement!: (angle: string) => void; updateAngle(_angle: number) {} + onClone = () => { + return this; + }; } diff --git a/src/BimModel/src/system/geometry/location/LocationLine.ts b/src/BimModel/src/system/geometry/location/LocationLine.ts index e0722fe..ae08c3d 100644 --- a/src/BimModel/src/system/geometry/location/LocationLine.ts +++ b/src/BimModel/src/system/geometry/location/LocationLine.ts @@ -83,8 +83,8 @@ export class LocationLine /** * */ - constructor(components: Components, private workPlane: THREE.Plane) { - super(components); + constructor(components: Components, workPlane: THREE.Plane) { + super(components, workPlane); this.init(); effect(() => { const {factor, toFixed} = lengthUnitSignal.value; @@ -161,6 +161,15 @@ export class LocationLine } onChangeLength!: (length: number) => void; onChangeLengthDomElement!: (length: string) => void; + updateMove(origin: THREE.Vector3, movingPoint: THREE.Vector3) { + const dir = getDirection(origin, movingPoint); + const dis = origin.distanceTo(movingPoint); + const start = this.location.start + .clone() + .add(dir.clone().multiplyScalar(dis)); + const end = this.location.end.clone().add(dir.clone().multiplyScalar(dis)); + this.update(start, end); + } updateLength(length: number) { if (!this.location) return; const {factor} = lengthUnitSignal.value; @@ -169,4 +178,9 @@ export class LocationLine const end = this.location.start.clone().add(dir.multiplyScalar(value)); this.update(this.location.start, end); } + onClone = () => { + const location = new LocationLine(this.components, this.workPlane); + location.update(this.location.start, this.location.end); + return location; + }; } diff --git a/src/BimModel/src/system/geometry/location/LocationPoint.ts b/src/BimModel/src/system/geometry/location/LocationPoint.ts index ff88606..0f80589 100644 --- a/src/BimModel/src/system/geometry/location/LocationPoint.ts +++ b/src/BimModel/src/system/geometry/location/LocationPoint.ts @@ -23,13 +23,20 @@ export class LocationPoint /** abstract @BaseLocation */ onSelect!: (select: boolean) => void; onHover!: (hover: boolean) => void; - onVisibility = (_visible: boolean) => {}; + onVisibility = (visible: boolean) => { + if (!this.point) return; + if (visible) { + this.components.annotationScene.add(this.point); + } else { + this.point.removeFromParent(); + } + }; point!: CSS2DObject; /** * */ - constructor(components: Components) { - super(components); + constructor(components: Components, workPlane: THREE.Plane) { + super(components, workPlane); this.point = createLabel(GeometryCSS.snap.endLine); } async dispose() { @@ -41,4 +48,7 @@ export class LocationPoint this.location.point.copy(point); this.point.position.copy(point); } + onClone = () => { + return this; + }; } diff --git a/src/BimModel/src/system/geometry/moving/index.ts b/src/BimModel/src/system/geometry/moving/index.ts new file mode 100644 index 0000000..b0d22e5 --- /dev/null +++ b/src/BimModel/src/system/geometry/moving/index.ts @@ -0,0 +1,67 @@ +import * as THREE from "three"; +import { + Components, + Disposable, + GeometryCSS, + getDirection, + lengthUnitSignal, + MaterialComponent, +} from "@BimModel/src"; +import {createLabel, disposeLabel, disposeSegment} from "../location/utils"; +import {CSS2DObject} from "three/examples/jsm/renderers/CSS2DRenderer"; +import {LocationUtils} from "../location/LocationUtils"; + +/** + * + */ +export class MovingLine implements Disposable { + private _visible = false; + set visible(visible: boolean) { + if (this._visible === visible) return; + this._visible = visible; + if (!this.segment || !this.tag) return; + if (visible) { + this.segment.add(this.tag); + this.components.annotationScene.add(this.segment); + } else { + this.tag.removeFromParent(); + this.segment.removeFromParent(); + } + } + segment!: THREE.Line; + tag!: CSS2DObject; + get AngleMaterial() { + return this.components.tools.get(MaterialComponent)?.AngleMaterial; + } + /** + * + */ + constructor(private components: Components) { + this.init(); + } + async dispose() { + disposeLabel(this.tag); + disposeSegment(this.segment); + } + private init() { + this.tag = createLabel(GeometryCSS.tag); + } + update(start: THREE.Vector3, end: THREE.Vector3) { + const mid = new THREE.Vector3().lerpVectors(start, end, 0.5); + const length = start.distanceTo(end); + const {factor, symbol, toFixed} = lengthUnitSignal.value; + this.tag.userData.content.textContent = `${(length * factor).toFixed( + toFixed + )} ${symbol}`; + this.tag.position.copy(mid); + this.tag.position.copy(mid); + const position = LocationUtils.getPositionLocationFromPoints([start, end]); + if (!this.segment) + this.segment = LocationUtils.createSegment( + this.AngleMaterial, + position, + 2 + ); + LocationUtils.updateLineSegmentPosition(position, this.segment); + } +}