/* $RCSfile: MakeGeometry.java,v $ * $Revision: 1.4 $ $Date: 2012/02/23 19:36:17 $ * Auth: Dave Loffredo (loffredo@steptools.com) * * Copyright (c) 1991-2012 by STEP Tools Inc. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation is hereby granted, provided that this copyright * notice and license appear on all copies of the software. * * STEP TOOLS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. STEP TOOLS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. */ import java.io.IOException; import java.util.UUID; import java.nio.ByteBuffer; import com.steptools.schemas.ifc2x3.*; import com.steptools.schemas.ifc2x3.Schema; import com.steptools.stdev.*; import com.steptools.stdev.p21.*; import com.steptools.stdev.keystone.*; public class MakeGeometry { // BUILD GEOMETRY - This IFC "hello world" program creates a building // then attaches unit contexts and an empty shape representation. Use // this as the starting point for a more sophisticated program. // This program creates the data set and saves it as a STEP Part 21 // file called "output_file.stp". This is a text file, so you can // look at it with a text editor or the STEP Part 21 file browser to // see how the information is represented. public static void main(String[] argv) throws STDevException, IOException { // Create a model to hold the instance data String output_name = "output_file.ifc"; Model mod = new Model(Schema.SCHEMA); // Give the model some Part 21 header information Header header = Header.forModel(mod); header.getFileName().setOriginating_system ("IFC Hello World"); ListString strs = new ListString(); strs.add("Empty Project Sample File"); header.getFileDescription().setDescription(strs); Population pop = (Population)mod.getPopulation(); //createBuildingWithGeometry(pop); // Make one shared representation context that gives the origin, // precision information, and the number of dimensions. See the // function below for the details. // IfcRepresentationContext ctx = makeGeometryContext(pop); // Make some ifc geometry to describe the building shape IfcShapeRepresentation shape = createBlockShape (pop, ctx); // Make a building with the shape representation IfcBuilding bldg = createBuildingWithGeometry (pop, shape); // Create the project instance for the file, then attach the // building as a child in the spatial structure // IfcProject proj = createProject (pop, ctx); IfcRelAggregates rel = createRelAggregates (pop, proj); rel.getRelatedObjects().addValue(bldg); Part21Writer writer = new Part21Writer(); writer.write(output_name, mod); } static IfcShapeRepresentation createBlockShape( Population pop, IfcRepresentationContext ctx ) { // CREATE A BLOCK SHAPE REPRESENTATION - Make and fill a // representation with geometry items that describe a block shape // for a building. // IfcShapeRepresentation rep = pop.newIfcShapeRepresentation(); rep.setRepresentationIdentifier ("Body"); rep.setRepresentationType ("SweptSolid"); rep.setContextOfItems (ctx); // Create a faceted brep 2001 Space Odyssey monolith with // height=9, width=4, and thickness=1 IfcCartesianPoint origin = makePoint (pop, 0,0,0); // Define a rectangular profile IfcRectangleProfileDef prof = pop.newIfcRectangleProfileDef(); prof.setProfileType(IfcProfileTypeEnum.AREA); prof.setProfileName(""); prof.setPosition (pop.newIfcAxis2Placement2D()); prof.getPosition().setLocation(origin); prof.setXDim (4.0); prof.setYDim (1.0); // sweep in the positive Z direction IfcDirection dir = pop.newIfcDirection(); ListReal rats = new ListReal(); rats.add(0.0); rats.add(0.0); rats.add(1.0); dir.setDirectionRatios(rats); IfcAxis2Placement3D ap = pop.newIfcAxis2Placement3D(); ap.setLocation(origin); // Solid that puts all of the pieces together IfcExtrudedAreaSolid sol = pop.newIfcExtrudedAreaSolid(); sol.setSweptArea (prof); sol.setPosition (ap); sol.setExtrudedDirection (dir); sol.setDepth (9); // add the solid to the representation rep.setItems(new SetIfcRepresentationItem()); rep.getItems().addValue(sol); return rep; } static IfcProject createProject ( Population pop, IfcRepresentationContext ctx ) { // CREATE A PROJECT - Create the project object for the file, // assign the units for the file, and geometry context. // IfcProject p = pop.newIfcProject(); p.setGlobalId (makeGuid()); p.setOwnerHistory (makeOwnerHistory(pop)); p.setName ("project"); p.setRepresentationContexts(new SetIfcRepresentationContext()); p.getRepresentationContexts().addValue(ctx); // Declare units so we know what the numbers in the geometry mean. // See the make_units.cxx file for details. // p.setUnitsInContext (makeUnits(pop)); return p; } static IfcBuilding createBuildingWithGeometry( Population pop, IfcShapeRepresentation rep ) { // CREATE THE BUILDING - Create a building object, and fill it in // with some sample information // IfcBuilding bldg = pop.newIfcBuilding(); bldg.setGlobalId (makeGuid()); bldg.setOwnerHistory (makeOwnerHistory(pop)); bldg.setName ("fortress of solitude"); bldg.setDescription ("secret lair"); //---------------------------------------- // ADD SHAPE REPRESENTATION AND ADD LOCAL PLACEMENT // // Attach the shape rep to the building using a product definition // shape instance, which can refer to one or more representations. // bldg.setRepresentation (pop.newIfcProductDefinitionShape()); ListIfcRepresentation reps = new ListIfcRepresentation(); reps.addValue(rep); bldg.getRepresentation().setRepresentations(reps); // Create an axis placement that describes the location and // orientation of the building geometry // IfcCartesianPoint p1 = makePoint (pop, 0, 0, 0); IfcAxis2Placement3D ap3d = pop.newIfcAxis2Placement3D(); ap3d.setLocation(p1); IfcLocalPlacement lp = pop.newIfcLocalPlacement(); lp.setPlacementRelTo(null); // The placement is referenced through a select type lp.setRelativePlacement (pop.newIfcAxis2Placement(ap3d)); bldg.setObjectPlacement(lp); return bldg; } static IfcRelAggregates createRelAggregates( Population pop, IfcObjectDefinition relating ) { // CREATE AGGREGATES RELATIONSHIP -- Assign the relating pointer // and then return the relationship so that we can add children to // the related attribute IfcRelAggregates rel = pop.newIfcRelAggregates(); rel.setGlobalId (makeGuid()); rel.setOwnerHistory (makeOwnerHistory(pop)); rel.setName (""); rel.setDescription (""); rel.setRelatingObject(relating); rel.setRelatedObjects(new SetIfcObjectDefinition()); return rel; } // ------------------------------------------------------------ // ------------------------------------------------------------ // SUPPORT FUNCTIONS -- Adjust these as needed // ------------------------------------------------------------ // ------------------------------------------------------------ static IfcGeometricRepresentationContext makeGeometryContext(Population pop) { // Create a geometric context IfcGeometricRepresentationContext ctx = pop.newIfcGeometricRepresentationContext(); // A file can have a "Model" context for the 3D view and a "Plan" // context for the 2D view // ctx.setContextIdentifier ("Model"); ctx.setContextType ("Design"); ctx.setCoordinateSpaceDimension (3); // geometric uncertainty for points ctx.setPrecision (1e-6); // Create a coordinate system origin for the context IfcCartesianPoint p1 = makePoint (pop, 0,0,0); IfcAxis2Placement3D ap3d = pop.newIfcAxis2Placement3D(); ap3d.setLocation(p1); // The placement is referenced through a select type ctx.setWorldCoordinateSystem (pop.newIfcAxis2Placement(ap3d)); return ctx; } static IfcCartesianPoint makePoint( Population pop, double x, double y, double z ) { IfcCartesianPoint loc = pop.newIfcCartesianPoint(); ListReal coords = new ListReal(); loc.setCoordinates(coords); coords.add(x); coords.add(y); coords.add(z); return loc; } static IfcOwnerHistory makeOwnerHistory(Population pop) { String shortcut = "__CONSTANT OWNER HISTORY"; // Use shortcut to an owner history if we have already created one Object prop = pop.getModel().getProperty(shortcut); if (prop != null) { return (IfcOwnerHistory) prop; } IfcOwnerHistory hist = pop.newIfcOwnerHistory(); IfcOrganization org = pop.newIfcOrganization(); org.setName ("Foo Bar, Inc."); IfcPerson person = pop.newIfcPerson(); person.setGivenName ("John"); person.setFamilyName ("Doe"); IfcPersonAndOrganization po = pop.newIfcPersonAndOrganization(); po.setThePerson (person); po.setTheOrganization (org); IfcApplication app = pop.newIfcApplication(); app.setApplicationDeveloper (org); app.setVersion ("1.0"); app.setApplicationFullName ("sample program"); app.setApplicationIdentifier ("program id"); hist.setOwningUser (po); hist.setOwningApplication (app); hist.setState (null); hist.setChangeAction (IfcChangeActionEnum.ADDED); // Optional fields hist.setLastModifiedDate (Primitives.NULL_INTEGER); hist.setLastModifyingUser (null); hist.setLastModifyingApplication (null); // IFC timestamp is seconds since 1970-01-01 (unix epoch) hist.setCreationDate ((int) (System.currentTimeMillis()/1000)); pop.getModel().addProperty(shortcut, hist); // save shortcut for future calls return hist; } static IfcUnitAssignment makeUnits(Population pop) { // Specify foot as the length unit, degree as the angle unit, // square feet as area. Since these are described in terms of the // SI units meter and radian, just trim this code if using meters // //---------------------------------------- // LENGTH UNIT //---------------------------------------- // SI unit for meter, we do not add this to the context, but we // use it to define the foot unit. If you are using meters, just // add this to the context and omit the code below that build the // conversion-based unit instance. // IfcSIUnit m = pop.newIfcSIUnit(); m.setUnitType (IfcUnitEnum.LENGTHUNIT); m.setPrefix (null); m.setName (IfcSIUnitName.METRE); // Foot is described as a conversion-based unit in based on the // meter SI unit. Also show dimensional exponents for the basic // quantities (length, time, mass, etc) the unit describes. // IfcMeasureWithUnit mwu = pop.newIfcMeasureWithUnit(); IfcValue val = pop.newIfcValueIfcLengthMeasure (0.3048); // cvt factor ft->m mwu.setValueComponent(val); mwu.setUnitComponent(pop.newIfcUnit(m)); IfcConversionBasedUnit ft = pop.newIfcConversionBasedUnit(); ft.setName ("FOOT"); ft.setUnitType (IfcUnitEnum.LENGTHUNIT); ft.setDimensions (pop.newIfcDimensionalExponents()); ft.getDimensions().setLengthExponent(1); ft.setConversionFactor (mwu); // Add foot to the unit context // The individual units are wrapped in a select type IfcUnit u = pop.newIfcUnit(ft); IfcUnitAssignment units = pop.newIfcUnitAssignment(); SetIfcUnit uset = new SetIfcUnit(); units.setUnits(uset); uset.addValue (u); //---------------------------------------- // ANGLE UNIT //---------------------------------------- // The SI angle unit is radians. Do not add this to the context, // but use it to define the degree unit. // IfcSIUnit rad = pop.newIfcSIUnit(); rad.setUnitType (IfcUnitEnum.PLANEANGLEUNIT); rad.setPrefix (null); rad.setName (IfcSIUnitName.RADIAN); // One Degree is Degree == PI/180 radians. Create the measure // with unit to indicate this conversion factor. We also need // dimensional exponents but these are all zero for angles. // mwu = pop.newIfcMeasureWithUnit(); mwu.setValueComponent ( pop.newIfcValueIfcPlaneAngleMeasure(0.01745329252) // PI/180 ); mwu.setUnitComponent (pop.newIfcUnit(rad)); IfcConversionBasedUnit deg = pop.newIfcConversionBasedUnit(); deg.setName ("DEGREE"); deg.setUnitType (IfcUnitEnum.PLANEANGLEUNIT); deg.setDimensions (pop.newIfcDimensionalExponents ()); deg.setConversionFactor (mwu); // Add degree to the unit context u = pop.newIfcUnit(deg); units.getUnits().addValue (u); //---------------------------------------- // AREA UNIT //---------------------------------------- // SI unit for square meters used to define square feet. If you // are using square meters, just add this to the context and omit // the code below that build the conversion-based unit instance. // IfcSIUnit sqm = pop.newIfcSIUnit(); rad.setUnitType (IfcUnitEnum.AREAUNIT); rad.setPrefix (null); rad.setName (IfcSIUnitName.SQUARE_METRE); // Foot is described as a conversion-based unit in based on the // meter SI unit. Also show dimensional exponents for the basic // quantities (length, time, mass, etc) the unit describes. // mwu = pop.newIfcMeasureWithUnit(); mwu.setValueComponent ( pop.newIfcValueIfcAreaMeasure(0.09290304) // convert sqft->sqm ); mwu.setUnitComponent (pop.newIfcUnit(sqm)); IfcConversionBasedUnit sqft = pop.newIfcConversionBasedUnit(); sqft.setName ("SQUARE FOOT"); sqft.setUnitType (IfcUnitEnum.AREAUNIT); sqft.setDimensions (pop.newIfcDimensionalExponents()); sqft.getDimensions().setLengthExponent(2); sqft.setConversionFactor (mwu); // Add degree to the unit context u = pop.newIfcUnit(deg); units.getUnits().addValue (u); return units; } // Base 64 encode a 128bit identifier using the character set and // approach given in the IFC documentation. // public static String makeGuid() { final String base64char = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$"; UUID uuid = UUID.randomUUID(); StringBuilder s = new StringBuilder(22); ByteBuffer b = ByteBuffer.allocate(16); b.putLong(0, uuid.getLeastSignificantBits()); b.putLong(7, uuid.getMostSignificantBits()); // first byte do manually because the first char is a partial // value. As per the sample code in the ifc documentation. // Note that Java uses >>> for unsigned shift and >> for // signed shift // s.append (base64char.charAt((b.get(0) & 0xc0) >>> 6)); s.append (base64char.charAt((b.get(0) & 0x3f))); // Break the remaining 15 bytes into three bytes groups, then for // each, divide into four 6bit values. for (int i=1; i<15; i+=3) { s.append (base64char.charAt((b.get(i) & 0xfc) >>> 2)); s.append (base64char.charAt( ((b.get(i) & 0x03) << 4) | ((b.get(i+1) & 0xf0) >>> 4) )); s.append (base64char.charAt( ((b.get(i+1) & 0x0f) << 2) | ((b.get(i+2) & 0xc0) >>> 6) )); s.append (base64char.charAt((b.get(i+2) & 0x3f))); } return s.toString(); } }