├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── Directory.Build.props ├── Directory.Build.targets ├── IxMilia.Step.sln ├── LICENSE.txt ├── README.md ├── build-and-test.cmd ├── build-and-test.sh ├── global.json ├── src ├── IxMilia.Step.Test │ ├── IxMilia.Step.Test.csproj │ ├── StepFileTests.cs │ ├── StepHeaderTests.cs │ ├── StepItemTests.cs │ ├── StepTestBase.cs │ └── StepTokenTests.cs ├── IxMilia.Step │ ├── Items │ │ ├── StepAdvancedFace.cs │ │ ├── StepAxis2Placement.cs │ │ ├── StepAxis2Placement2D.cs │ │ ├── StepAxis2Placement3D.cs │ │ ├── StepBSplineCurve.cs │ │ ├── StepBSplineCurveWithKnots.cs │ │ ├── StepBoundedCurve.cs │ │ ├── StepCartesianPoint.cs │ │ ├── StepCircle.cs │ │ ├── StepConic.cs │ │ ├── StepCurve.cs │ │ ├── StepCylindricalSurface.cs │ │ ├── StepDirection.cs │ │ ├── StepEdge.cs │ │ ├── StepEdgeCurve.cs │ │ ├── StepEdgeLoop.cs │ │ ├── StepElementarySurface.cs │ │ ├── StepEllipse.cs │ │ ├── StepFace.cs │ │ ├── StepFaceBound.cs │ │ ├── StepFaceOuterBound.cs │ │ ├── StepFaceSurface.cs │ │ ├── StepGeometricRepresentationItem.cs │ │ ├── StepItemType.cs │ │ ├── StepLine.cs │ │ ├── StepLoop.cs │ │ ├── StepOrientedEdge.cs │ │ ├── StepPlacement.cs │ │ ├── StepPlane.cs │ │ ├── StepPoint.cs │ │ ├── StepRepresentationItem.cs │ │ ├── StepRepresentationItem_FromTypedParameter.cs │ │ ├── StepSurface.cs │ │ ├── StepTopologicalRepresentationItem.cs │ │ ├── StepTriple.cs │ │ ├── StepVector.cs │ │ ├── StepVertex.cs │ │ └── StepVertexPoint.cs │ ├── IxMilia.Step.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── StepBinder.cs │ ├── StepBoundItem.cs │ ├── StepFile.cs │ ├── StepLexer.cs │ ├── StepReadException.cs │ ├── StepReader.cs │ ├── StepSchemaTypes.cs │ ├── StepTokenizer.cs │ ├── StepWriter.cs │ ├── Syntax │ │ ├── StepAutoSyntax.cs │ │ ├── StepComplexItemSyntax.cs │ │ ├── StepDataSectionSyntax.cs │ │ ├── StepEntityInstanceReferenceSyntax.cs │ │ ├── StepEntityInstanceSyntax.cs │ │ ├── StepEnumerationValueSyntax.cs │ │ ├── StepFileSyntax.cs │ │ ├── StepHeaderMacroSyntax.cs │ │ ├── StepHeaderSectionSyntax.cs │ │ ├── StepIntegerSyntax.cs │ │ ├── StepItemSyntax.cs │ │ ├── StepOmittedSyntax.cs │ │ ├── StepRealSyntax.cs │ │ ├── StepSimpleItemSyntax.cs │ │ ├── StepStringSyntax.cs │ │ ├── StepSyntax.cs │ │ ├── StepSyntaxExtensions.cs │ │ ├── StepSyntaxList.cs │ │ └── StepSyntaxType.cs │ └── Tokens │ │ ├── StepAsteriskToken.cs │ │ ├── StepCommaToken.cs │ │ ├── StepConstantInstanceToken.cs │ │ ├── StepConstantValueToken.cs │ │ ├── StepEntityInstanceToken.cs │ │ ├── StepEnumerationToken.cs │ │ ├── StepEqualsToken.cs │ │ ├── StepInstanceValueToken.cs │ │ ├── StepIntegerToken.cs │ │ ├── StepKeywordToken.cs │ │ ├── StepLeftParenToken.cs │ │ ├── StepOmittedToken.cs │ │ ├── StepRealToken.cs │ │ ├── StepRightParenToken.cs │ │ ├── StepSemiColonToken.cs │ │ ├── StepStringToken.cs │ │ ├── StepToken.cs │ │ └── StepTokenKind.cs ├── create-package.cmd └── create-package.sh └── version.txt /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | build: 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest] 12 | configuration: [Debug, Release] 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-dotnet@v4 16 | - name: Build and test 17 | shell: pwsh 18 | run: | 19 | $shellExt = if ($IsWindows) { "cmd" } else { "sh" } 20 | & ./build-and-test.$shellExt --configuration ${{ matrix.configuration }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore VS files 2 | *.suo 3 | *.user 4 | .vs/ 5 | 6 | # ignore artifacts 7 | artifacts/ 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceRoot}/src/IxMilia.Step.Test/bin/Debug/netcoreapp1.0/IxMilia.Step.Test.dll", 14 | "args": [], 15 | "cwd": "${workspaceRoot}/src/IxMilia.Step.Test", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }, 21 | { 22 | "name": ".NET Core Attach", 23 | "type": "coreclr", 24 | "request": "attach", 25 | "processId": "${command:pickProcess}" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "command": "dotnet", 4 | "args": [], 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "type": "shell", 9 | "command": "dotnet", 10 | "args": [ 11 | "build", 12 | "${workspaceRoot}/src/IxMilia.Step.Test/IxMilia.Step.Test.csproj" 13 | ], 14 | "problemMatcher": "$msCompile", 15 | "group": { 16 | "_id": "build", 17 | "isDefault": false 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)version.txt').Trim()) 5 | $(MSBuildThisFileDirectory)artifacts 6 | $(ArtifactsDir)\packages 7 | $(ArtifactsDir)\bin\$(MSBuildProjectName) 8 | $(ArtifactsDir)\obj\$(MSBuildProjectName) 9 | $(ArtifactsPackagesDir)\$(Configuration) 10 | embedded 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(Version) 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /IxMilia.Step.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IxMilia.Step", "src\IxMilia.Step\IxMilia.Step.csproj", "{7BEB4FB6-8A3E-4690-B06C-357130AB4226}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IxMilia.Step.Test", "src\IxMilia.Step.Test\IxMilia.Step.Test.csproj", "{078EDA8D-F0BB-4D60-A7BD-1782A9CF713C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {7BEB4FB6-8A3E-4690-B06C-357130AB4226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {7BEB4FB6-8A3E-4690-B06C-357130AB4226}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {7BEB4FB6-8A3E-4690-B06C-357130AB4226}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {7BEB4FB6-8A3E-4690-B06C-357130AB4226}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {078EDA8D-F0BB-4D60-A7BD-1782A9CF713C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {078EDA8D-F0BB-4D60-A7BD-1782A9CF713C}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {078EDA8D-F0BB-4D60-A7BD-1782A9CF713C}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {078EDA8D-F0BB-4D60-A7BD-1782A9CF713C}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) IxMilia. 4 | All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IxMilia.Step 2 | ============ 3 | 4 | A portable .NET library for reading and writing STEP CAD files. 5 | 6 | ## Usage 7 | 8 | Open a STEP file: 9 | 10 | ``` C# 11 | using System.IO; 12 | using IxMilia.Step; 13 | using IxMilia.Step.Items; 14 | // ... 15 | 16 | //------------------------------------------------------------ read from a file 17 | StepFile stepFile; 18 | using (FileStream fs = new FileStream(@"C:\Path\To\File.stp", FileMode.Open)) 19 | { 20 | stepFile = StepFile.Load(fs); 21 | } 22 | 23 | // if on >= NETStandard1.3 you can use: 24 | // StepFile stepFile = StepFile.Load(@"C:\Path\To\File.stp"); 25 | 26 | //---------------------------------------------- or read directly from a string 27 | StepFile stepFile = StepFile.Parse(@"ISO-10303-21; 28 | HEADER; 29 | ... 30 | END-ISO-103030-21;"); 31 | //----------------------------------------------------------------------------- 32 | 33 | foreach (StepRepresentationItem item in stepFile.Items) 34 | { 35 | switch (item.ItemType) 36 | { 37 | case StepItemType.Line: 38 | StepLine line = (StepLine)item; 39 | // ... 40 | break; 41 | // ... 42 | } 43 | } 44 | ``` 45 | 46 | Save a STEP file: 47 | 48 | ``` C# 49 | using System.IO; 50 | using IxMilia.Step; 51 | using IxMilia.Step.Items; 52 | // ... 53 | 54 | StepFile stepFile = new StepFile(); 55 | stepFile.Items.Add(new StepDirection("direction-label", 1.0, 0.0, 0.0)); 56 | // ... 57 | 58 | //------------------------------------------------------------- write to a file 59 | using (FileStream fs = new FileStream(@"C:\Path\To\File.stp", FileMode.Create)) 60 | { 61 | stepFile.Save(fs); 62 | } 63 | 64 | // if on >= NETStandard1.3 you can use 65 | // stepFile.Save(@"C:\Path\To\File.stp"); 66 | 67 | //------------------------------------------------------- or output as a string 68 | string contents = stepFile.GetContentsAsString(); 69 | ``` 70 | 71 | ## Building locally 72 | 73 | To build locally, install the [latest .NET Core 3.0 SDK](https://dotnet.microsoft.com/download). 74 | 75 | ## Specification 76 | 77 | Using spec from steptools.com [here](http://www.steptools.com/library/standard/IS_final_p21e3.html). 78 | 79 | STEP Application Protocols [here](http://www.steptools.com/support/stdev_docs/express/). 80 | -------------------------------------------------------------------------------- /build-and-test.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set thisdir=%~dp0 5 | set configuration=Debug 6 | set runtests=true 7 | 8 | :parseargs 9 | if "%1" == "" goto argsdone 10 | if /i "%1" == "-c" goto set_configuration 11 | if /i "%1" == "--configuration" goto set_configuration 12 | if /i "%1" == "-notest" goto set_notest 13 | if /i "%1" == "--notest" goto set_notest 14 | 15 | echo Unsupported argument: %1 16 | goto error 17 | 18 | :set_configuration 19 | set configuration=%2 20 | shift 21 | shift 22 | goto parseargs 23 | 24 | :set_notest 25 | set runtests=false 26 | shift 27 | goto parseargs 28 | 29 | :argsdone 30 | 31 | :: build 32 | dotnet restore 33 | if errorlevel 1 exit /b 1 34 | dotnet build -c %configuration% 35 | if errorlevel 1 exit /b 1 36 | 37 | :: test 38 | if /i "%runtests%" == "true" ( 39 | dotnet test -c %configuration% --no-restore --no-build 40 | if errorlevel 1 goto error 41 | ) 42 | -------------------------------------------------------------------------------- /build-and-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | _SCRIPT_DIR="$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )" 4 | 5 | CONFIGURATION=Debug 6 | RUNTESTS=true 7 | 8 | while [ $# -gt 0 ]; do 9 | case "$1" in 10 | --configuration|-c) 11 | CONFIGURATION=$2 12 | shift 13 | ;; 14 | --notest) 15 | RUNTESTS=false 16 | ;; 17 | *) 18 | echo "Invalid argument: $1" 19 | exit 1 20 | ;; 21 | esac 22 | shift 23 | done 24 | 25 | # build 26 | dotnet restore 27 | dotnet build -c $CONFIGURATION 28 | 29 | # test 30 | if [ "$RUNTESTS" = "true" ]; then 31 | dotnet test -c $CONFIGURATION --no-restore --no-build 32 | fi 33 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.300", 4 | "rollForward": "latestMinor" 5 | } 6 | } -------------------------------------------------------------------------------- /src/IxMilia.Step.Test/IxMilia.Step.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/IxMilia.Step.Test/StepFileTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using IxMilia.Step.Items; 4 | using Xunit; 5 | 6 | namespace IxMilia.Step.Test 7 | { 8 | public class StepFileTests : StepTestBase 9 | { 10 | [Fact] 11 | public void FileSystemAPITest() 12 | { 13 | var filePath = Path.GetTempFileName(); 14 | var stepFile = new StepFile(); 15 | var point = new StepCartesianPoint("some-label", 1.0, 2.0, 3.0); 16 | stepFile.Items.Add(point); 17 | 18 | // round trip 19 | stepFile.Save(filePath); 20 | var stepFile2 = StepFile.Load(filePath); 21 | 22 | var point2 = (StepCartesianPoint)stepFile2.Items.Single(); 23 | Assert.Equal(point.Name, point2.Name); 24 | Assert.Equal(point.X, point2.X); 25 | Assert.Equal(point.Y, point2.Y); 26 | Assert.Equal(point.Z, point2.Z); 27 | 28 | try 29 | { 30 | File.Delete(filePath); 31 | } 32 | catch 33 | { 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/IxMilia.Step.Test/StepHeaderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Xunit; 4 | 5 | namespace IxMilia.Step.Test 6 | { 7 | public class StepHeaderTests : StepTestBase 8 | { 9 | private StepFile ReadFileFromHeader(string header) 10 | { 11 | var file = $@" 12 | {StepFile.MagicHeader}; 13 | {StepFile.HeaderText}; 14 | {header.Trim()} 15 | {StepFile.EndSectionText}; 16 | {StepFile.DataText}; 17 | {StepFile.EndSectionText}; 18 | {StepFile.MagicFooter}; 19 | "; 20 | return StepFile.Parse(file.Trim()); 21 | } 22 | 23 | [Fact] 24 | public void FileDescriptionTest() 25 | { 26 | var file = ReadFileFromHeader("FILE_DESCRIPTION(('some description'), '2;1');"); 27 | Assert.Equal("some description", file.Description); 28 | Assert.Equal("2;1", file.ImplementationLevel); 29 | } 30 | 31 | [Fact] 32 | public void FileDescriptionWithMultiplePartsTest() 33 | { 34 | var file = ReadFileFromHeader("FILE_DESCRIPTION(('some description', ', ', 'more description'), '2;1');"); 35 | Assert.Equal("some description, more description", file.Description); 36 | Assert.Equal("2;1", file.ImplementationLevel); 37 | } 38 | 39 | [Fact] 40 | public void FullHeaderTest() 41 | { 42 | var file = ReadFileFromHeader(@" 43 | FILE_DESCRIPTION(('description'), '2;1'); 44 | FILE_NAME('file-name', '2010-01-01T', ('author'), ('organization'), 'preprocessor', 'originator', 'authorization'); 45 | FILE_SCHEMA(('EXPLICIT_DRAUGHTING')); 46 | "); 47 | Assert.Equal("description", file.Description); 48 | Assert.Equal("2;1", file.ImplementationLevel); 49 | Assert.Equal("file-name", file.Name); 50 | Assert.Equal(new DateTime(2010, 1, 1), file.Timestamp); 51 | Assert.Equal("author", file.Author); 52 | Assert.Equal("organization", file.Organization); 53 | Assert.Equal("preprocessor", file.PreprocessorVersion); 54 | Assert.Equal("originator", file.OriginatingSystem); 55 | Assert.Equal("authorization", file.Authorization); 56 | Assert.Equal(StepSchemaTypes.ExplicitDraughting, file.Schemas.Single()); 57 | } 58 | 59 | [Fact] 60 | public void ReadDifferentTimeStampsTest() 61 | { 62 | var file = ReadFileFromHeader(@"FILE_NAME('', '2016-06-26T13:59:52+02:00', (), (), '', '', '');"); 63 | Assert.Equal(2016, file.Timestamp.Year); 64 | Assert.Equal(6, file.Timestamp.Month); 65 | } 66 | 67 | [Fact] 68 | public void ReadTimeStampMonthWithoutLeadingZerosTest() 69 | { 70 | var file = ReadFileFromHeader(@"FILE_NAME('', '2004-3-17T2:58:55 PM+8:00', (), (), '', '', '');"); 71 | Assert.Equal(2004, file.Timestamp.Year); 72 | Assert.Equal(3, file.Timestamp.Month); 73 | } 74 | 75 | [Fact] 76 | public void WriteHeaderTest() 77 | { 78 | var file = new StepFile(); 79 | file.Description = "some description"; 80 | file.ImplementationLevel = "2;1"; 81 | file.Name = "file-name"; 82 | file.Timestamp = new DateTime(2010, 1, 1); 83 | file.Author = "author"; 84 | file.Organization = "organization"; 85 | file.PreprocessorVersion = "preprocessor"; 86 | file.OriginatingSystem = "originator"; 87 | file.Authorization = "authorization"; 88 | file.Schemas.Add(StepSchemaTypes.ExplicitDraughting); 89 | AssertFileIs(file, @" 90 | ISO-10303-21; 91 | HEADER; 92 | FILE_DESCRIPTION(('some description'),'2;1'); 93 | FILE_NAME('file-name','2010-01-01T00:00:00.0000000',('author'),('organization'),'preprocessor','originator','authorization'); 94 | FILE_SCHEMA(('EXPLICIT_DRAUGHTING')); 95 | ENDSEC; 96 | DATA; 97 | ENDSEC; 98 | END-ISO-10303-21; 99 | ".TrimStart()); 100 | } 101 | 102 | [Fact] 103 | public void WriteHeaderWithLongDescriptionTest() 104 | { 105 | var file = new StepFile(); 106 | file.Description = new string('a', 257); 107 | file.Timestamp = new DateTime(2010, 1, 1); 108 | file.Schemas.Add(StepSchemaTypes.ExplicitDraughting); 109 | AssertFileIs(file, $@" 110 | ISO-10303-21; 111 | HEADER; 112 | FILE_DESCRIPTION(('{new string('a', 256)}','a'),''); 113 | FILE_NAME('','2010-01-01T00:00:00.0000000',(''),(''),'','',''); 114 | FILE_SCHEMA(('EXPLICIT_DRAUGHTING')); 115 | ENDSEC; 116 | DATA; 117 | ENDSEC; 118 | END-ISO-10303-21; 119 | ".TrimStart()); 120 | } 121 | 122 | [Fact] 123 | public void ReadHeaderWithUnsupportedSchemaTest() 124 | { 125 | var file = ReadFileFromHeader(@"FILE_SCHEMA(('EXPLICIT_DRAUGHTING','UNSUPPORTED_SCHEMA'));"); 126 | Assert.Single(file.Schemas); 127 | Assert.Single(file.UnsupportedSchemas); 128 | } 129 | 130 | [Fact] 131 | public void WriteHeaderWithUnsupportedSchemaTest() 132 | { 133 | var file = new StepFile(); 134 | file.UnsupportedSchemas.Add("UNSUPPORTED_SCHEMA"); 135 | AssertFileContains(file, "FILE_SCHEMA(('UNSUPPORTED_SCHEMA'));"); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/IxMilia.Step.Test/StepItemTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using IxMilia.Step.Items; 3 | using Xunit; 4 | 5 | namespace IxMilia.Step.Test 6 | { 7 | public class StepItemTests : StepTestBase 8 | { 9 | private StepFile ReadFile(string data) 10 | { 11 | var text = $@" 12 | ISO-10303-21; 13 | HEADER; 14 | ENDSEC; 15 | DATA; 16 | {data.Trim()} 17 | ENDSEC; 18 | END-ISO-10303-21; 19 | "; 20 | var file = StepFile.Parse(text.Trim()); 21 | return file; 22 | } 23 | 24 | private StepRepresentationItem ReadTopLevelItem(string data) 25 | { 26 | var file = ReadFile(data); 27 | return file.GetTopLevelItems().Single(); 28 | } 29 | 30 | private void AssertFileContains(StepRepresentationItem item, string expected) 31 | { 32 | var file = new StepFile(); 33 | file.Items.Add(item); 34 | AssertFileContains(file, expected); 35 | } 36 | 37 | [Fact] 38 | public void ReadCartesianPointTest1() 39 | { 40 | var point = (StepCartesianPoint)ReadTopLevelItem("#1=CARTESIAN_POINT('name',(1.0,2.0,3.0));"); 41 | Assert.Equal("name", point.Name); 42 | Assert.Equal(1.0, point.X); 43 | Assert.Equal(2.0, point.Y); 44 | Assert.Equal(3.0, point.Z); 45 | } 46 | 47 | [Fact] 48 | public void ReadCartesianPointTest2() 49 | { 50 | var point = (StepCartesianPoint)ReadTopLevelItem("#1=CARTESIAN_POINT('name',(1.0));"); 51 | Assert.Equal("name", point.Name); 52 | Assert.Equal(1.0, point.X); 53 | Assert.Equal(0.0, point.Y); 54 | Assert.Equal(0.0, point.Z); 55 | } 56 | 57 | [Fact] 58 | public void ReadCartesianPointWithOmittedNameTest() 59 | { 60 | var point = (StepCartesianPoint)ReadTopLevelItem("#1=CARTESIAN_POINT($,(0.0,0.0,0.0));"); 61 | Assert.Equal(string.Empty, point.Name); 62 | } 63 | 64 | [Fact] 65 | public void ReadDirectionTest() 66 | { 67 | var direction = (StepDirection)ReadTopLevelItem("#1=DIRECTION('name',(1.0,2.0,3.0));"); 68 | Assert.Equal("name", direction.Name); 69 | Assert.Equal(1.0, direction.X); 70 | Assert.Equal(2.0, direction.Y); 71 | Assert.Equal(3.0, direction.Z); 72 | } 73 | 74 | [Fact] 75 | public void ReadSubReferencedItemTest() 76 | { 77 | var vector = (StepVector)ReadTopLevelItem("#1=VECTOR('name',DIRECTION('',(0.0,0.0,1.0)),15.0);"); 78 | Assert.Equal(new StepDirection("", 0.0, 0.0, 1.0), vector.Direction); 79 | Assert.Equal(15.0, vector.Length); 80 | } 81 | 82 | [Fact] 83 | public void ReadPreviouslyReferencedItemsTest() 84 | { 85 | var vector = (StepVector)ReadTopLevelItem(@" 86 | #1=DIRECTION('',(0.0,0.0,1.0)); 87 | #2=VECTOR('',#1,15.0); 88 | "); 89 | Assert.Equal(new StepDirection("", 0.0, 0.0, 1.0), vector.Direction); 90 | Assert.Equal(15.0, vector.Length); 91 | } 92 | 93 | [Fact] 94 | public void ReadPostReferencedItemTest() 95 | { 96 | var vector = (StepVector)ReadTopLevelItem(@" 97 | #1=VECTOR('',#2,15.0); 98 | #2=DIRECTION('',(0.0,0.0,1.0)); 99 | "); 100 | Assert.Equal(new StepDirection("", 0.0, 0.0, 1.0), vector.Direction); 101 | Assert.Equal(15.0, vector.Length); 102 | } 103 | 104 | [Fact] 105 | public void ReadLineTest() 106 | { 107 | var line = (StepLine)ReadTopLevelItem(@" 108 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 109 | #2=DIRECTION('',(0.0,0.0,1.0)); 110 | #3=VECTOR('',#2,15.0); 111 | #4=LINE('',#1,#3); 112 | "); 113 | Assert.Equal(new StepCartesianPoint("", 1.0, 2.0, 3.0), line.Point); 114 | Assert.Equal(15.0, line.Vector.Length); 115 | Assert.Equal(new StepDirection("", 0.0, 0.0, 1.0), line.Vector.Direction); 116 | } 117 | 118 | [Fact] 119 | public void WriteLineTest() 120 | { 121 | var file = new StepFile(); 122 | file.Items.Add(new StepLine("", new StepCartesianPoint("", 1.0, 2.0, 3.0), new StepVector("", new StepDirection("", 1.0, 0.0, 0.0), 4.0))); 123 | AssertFileContains(file, @" 124 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 125 | #2=DIRECTION('',(1.0,0.0,0.0)); 126 | #3=VECTOR('',#2,4.0); 127 | #4=LINE('',#1,#3); 128 | "); 129 | } 130 | 131 | [Fact] 132 | public void WriteLineWithInlineReferencesTest() 133 | { 134 | var file = new StepFile(); 135 | file.Items.Add(new StepLine("", new StepCartesianPoint("", 1.0, 2.0, 3.0), new StepVector("", new StepDirection("", 1.0, 0.0, 0.0), 4.0))); 136 | AssertFileContains(file, @" 137 | #1=LINE('',CARTESIAN_POINT('',(1.0,2.0,3.0)),VECTOR('',DIRECTION('',(1.0,0.0,0.0 138 | )),4.0)); 139 | ", inlineReferences: true); 140 | } 141 | 142 | [Fact] 143 | public void ReadCircleTest() 144 | { 145 | var circle = (StepCircle)ReadTopLevelItem(@" 146 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 147 | #2=DIRECTION('',(0.0,0.0,1.0)); 148 | #3=AXIS2_PLACEMENT_2D('',#1,#2); 149 | #4=CIRCLE('',#3,5.0); 150 | "); 151 | Assert.Equal(new StepCartesianPoint("", 1.0, 2.0, 3.0), ((StepAxis2Placement2D)circle.Position).Location); 152 | Assert.Equal(new StepDirection("", 0.0, 0.0, 1.0), ((StepAxis2Placement2D)circle.Position).RefDirection); 153 | Assert.Equal(5.0, circle.Radius); 154 | } 155 | 156 | [Fact] 157 | public void WriteCircleTest() 158 | { 159 | var circle = new StepCircle("", new StepAxis2Placement2D("", new StepCartesianPoint("", 1.0, 2.0, 3.0), new StepDirection("", 0.0, 0.0, 1.0)), 5.0); 160 | AssertFileContains(circle, @" 161 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 162 | #2=DIRECTION('',(0.0,0.0,1.0)); 163 | #3=AXIS2_PLACEMENT_2D('',#1,#2); 164 | #4=CIRCLE('',#3,5.0); 165 | "); 166 | } 167 | 168 | [Fact] 169 | public void ReadEllipseTest() 170 | { 171 | var ellipse = (StepEllipse)ReadTopLevelItem(@" 172 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 173 | #2=DIRECTION('',(0.0,0.0,1.0)); 174 | #3=AXIS2_PLACEMENT_2D('',#1,#2); 175 | #4=ELLIPSE('',#3,3.0,4.0); 176 | "); 177 | Assert.Equal(new StepCartesianPoint("", 1.0, 2.0, 3.0), ellipse.Position.Location); 178 | Assert.Equal(new StepDirection("", 0.0, 0.0, 1.0), ellipse.Position.RefDirection); 179 | Assert.Equal(3.0, ellipse.SemiAxis1); 180 | Assert.Equal(4.0, ellipse.SemiAxis2); 181 | } 182 | 183 | [Fact] 184 | public void ReadTopLevelReferencedItemsTest() 185 | { 186 | var file = ReadFile(@" 187 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 188 | #2=DIRECTION('',(0.0,0.0,1.0)); 189 | #3=AXIS2_PLACEMENT_2D('',#1,#2); 190 | #4=ELLIPSE('',#3,3.0,4.0); 191 | "); 192 | 193 | Assert.Equal(4, file.Items.Count); 194 | 195 | // only ELLIPSE() isn't referenced by another item 196 | var ellipse = (StepEllipse)file.GetTopLevelItems().Single(); 197 | } 198 | 199 | [Fact] 200 | public void ReadTopLevelInlinedItemsTest() 201 | { 202 | var file = ReadFile("#1=ELLIPSE('',AXIS2_PLACEMENT_2D('',CARTESIAN_POINT('',(1.0,2.0,3.0)),DIRECTION('',(0.0,0.0,1.0))),3.0,4.0);"); 203 | 204 | Assert.Single(file.Items); 205 | 206 | // only ELLIPSE() isn't referenced by another item 207 | var ellipse = (StepEllipse)file.GetTopLevelItems().Single(); 208 | } 209 | 210 | [Fact] 211 | public void ReadBSplineWithKnotsItemsTest() 212 | { 213 | var spline = (StepBSplineCurveWithKnots)ReadTopLevelItem(@" 214 | #1=CARTESIAN_POINT('Ctrl Pts',(-2.09228759117738,32.4775276519752,7.66388871568773)); 215 | #2=CARTESIAN_POINT('Ctrl Pts',(-2.09228759389655,30.5382976972817,7.66388872564781)); 216 | #3=CARTESIAN_POINT('Ctrl Pts',(-2.09228913953809,28.5997370344523,7.66389438721404)); 217 | #4=CARTESIAN_POINT('Ctrl Pts',(-2.09228986816456,26.5173645163537,7.66389705611683)); 218 | #5=CARTESIAN_POINT('Ctrl Pts',(-2.09228981902238,26.4462306775892,7.6638968761128)); 219 | #6=CARTESIAN_POINT('Ctrl Pts',(-2.0922902432834,25.9015378442672,7.66389843014835)); 220 | #7=CARTESIAN_POINT('Ctrl Pts',(-2.11494520023805,24.945781133428,7.74688179710576)); 221 | #8=CARTESIAN_POINT('Ctrl Pts',(-2.21905874762543,23.5389919187115,8.1282417231202)); 222 | #9=CARTESIAN_POINT('Ctrl Pts',(-2.39215391928761,22.2144888644552,8.76227603964325)); 223 | #10=CARTESIAN_POINT('Ctrl Pts',(-2.62666223390231,21.041382550633,9.62126198100665)); 224 | #11=CARTESIAN_POINT('Ctrl Pts',(-2.81820100260438,20.3836711077483,10.3228537766386)); 225 | #12=CARTESIAN_POINT('Ctrl Pts',(-2.91923318155533,20.0960030522361,10.6929268867829)); 226 | #13=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12) 227 | ,.UNSPECIFIED.,.F.,.F.,(4,2,2,1,1,1,1,4),(0.00162506910839039,0.4223270995939, 228 | 0.437186866643407,0.53596295034332,0.634739034043234,0.733515117743147, 229 | 0.832291201443061,0.927367642384199),.UNSPECIFIED.); 230 | "); 231 | Assert.Equal(12, spline.ControlPointsList.Count); 232 | Assert.Equal(8, spline.Knots.Count); 233 | Assert.Equal(8, spline.KnotMultiplicities.Count); 234 | Assert.Equal(3, spline.Degree); 235 | } 236 | 237 | [Fact] 238 | public void WriteEllipseTest() 239 | { 240 | var ellipse = new StepEllipse("", new StepAxis2Placement2D("", new StepCartesianPoint("", 1.0, 2.0, 3.0), new StepDirection("", 0.0, 0.0, 1.0)), 3.0, 4.0); 241 | AssertFileContains(ellipse, @" 242 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 243 | #2=DIRECTION('',(0.0,0.0,1.0)); 244 | #3=AXIS2_PLACEMENT_2D('',#1,#2); 245 | #4=ELLIPSE('',#3,3.0,4.0); 246 | "); 247 | } 248 | 249 | [Fact] 250 | public void ReadEdgeCurveTest() 251 | { 252 | var edgeCurve = (StepEdgeCurve)ReadTopLevelItem(@" 253 | #1=CIRCLE('',AXIS2_PLACEMENT_2D('',CARTESIAN_POINT('',(0.0,0.0,0.0)),DIRECTION('',(0.0,0.0,1.0))),5.0); 254 | #2=EDGE_CURVE('',VERTEX_POINT('',CARTESIAN_POINT('',(1.0,2.0,3.0))),VERTEX_POINT('',CARTESIAN_POINT('',(4.0,5.0,6.0))),#1,.T.); 255 | "); 256 | Assert.IsType(edgeCurve.EdgeGeometry); 257 | Assert.True(edgeCurve.IsSameSense); 258 | } 259 | 260 | [Fact] 261 | public void WriteEdgeCurveTest() 262 | { 263 | var edgeCurve = new StepEdgeCurve( 264 | "", 265 | new StepVertexPoint("", new StepCartesianPoint("", 1.0, 2.0, 3.0)), 266 | new StepVertexPoint("", new StepCartesianPoint("", 4.0, 5.0, 6.0)), 267 | new StepCircle("", 268 | new StepAxis2Placement2D("", new StepCartesianPoint("", 7.0, 8.0, 9.0), new StepDirection("", 0.0, 0.0, 1.0)), 269 | 5.0), 270 | true); 271 | AssertFileContains(edgeCurve, @" 272 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 273 | #2=VERTEX_POINT('',#1); 274 | #3=CARTESIAN_POINT('',(4.0,5.0,6.0)); 275 | #4=VERTEX_POINT('',#3); 276 | #5=CARTESIAN_POINT('',(7.0,8.0,9.0)); 277 | #6=DIRECTION('',(0.0,0.0,1.0)); 278 | #7=AXIS2_PLACEMENT_2D('',#5,#6); 279 | #8=CIRCLE('',#7,5.0); 280 | #9=EDGE_CURVE('',#2,#4,#8,.T.); 281 | "); 282 | } 283 | 284 | [Fact] 285 | public void ReadPlaneTest() 286 | { 287 | var plane = (StepPlane)ReadTopLevelItem(@" 288 | #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); 289 | #2=DIRECTION('',(0.0,0.0,1.0)); 290 | #3=DIRECTION('',(1.0,0.0,0.0)); 291 | #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); 292 | #5=PLANE('',#4); 293 | "); 294 | } 295 | 296 | [Fact] 297 | public void WritePlaneTest() 298 | { 299 | var plane = new StepPlane( 300 | "", 301 | new StepAxis2Placement3D("", new StepCartesianPoint("", 1.0, 2.0, 3.0), new StepDirection("", 0.0, 0.0, 1.0), new StepDirection("", 1.0, 0.0, 0.0))); 302 | AssertFileContains(plane, @" 303 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 304 | #2=DIRECTION('',(0.0,0.0,1.0)); 305 | #3=DIRECTION('',(1.0,0.0,0.0)); 306 | #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); 307 | #5=PLANE('',#4); 308 | "); 309 | } 310 | 311 | [Fact] 312 | public void ReadOrientedEdgeTest() 313 | { 314 | var orientedEdge = (StepOrientedEdge)ReadTopLevelItem(@" 315 | #1=CIRCLE('',AXIS2_PLACEMENT_2D('',CARTESIAN_POINT('',(0.0,0.0,0.0)),DIRECTION('',(0.0,0.0,1.0))),5.0); 316 | #2=EDGE_CURVE('',VERTEX_POINT('',CARTESIAN_POINT('',(1.0,2.0,3.0))),VERTEX_POINT('',CARTESIAN_POINT('',(4.0,5.0,6.0))),#1,.T.); 317 | #3=ORIENTED_EDGE('',*,*,#2,.T.); 318 | "); 319 | Assert.True(orientedEdge.Orientation); 320 | } 321 | 322 | [Fact] 323 | public void WriteOrientedEdgeTest() 324 | { 325 | var orientedEdge = new StepOrientedEdge( 326 | "", 327 | null, 328 | null, 329 | new StepEdgeCurve( 330 | "", 331 | new StepVertexPoint("", new StepCartesianPoint("", 1.0, 2.0, 3.0)), 332 | new StepVertexPoint("", new StepCartesianPoint("", 4.0, 5.0, 6.0)), 333 | new StepCircle("", 334 | new StepAxis2Placement2D("", new StepCartesianPoint("", 7.0, 8.0, 9.0), new StepDirection("", 0.0, 0.0, 1.0)), 335 | 5.0), 336 | true), 337 | true); 338 | AssertFileContains(orientedEdge, @" 339 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 340 | #2=VERTEX_POINT('',#1); 341 | #3=CARTESIAN_POINT('',(4.0,5.0,6.0)); 342 | #4=VERTEX_POINT('',#3); 343 | #5=CARTESIAN_POINT('',(7.0,8.0,9.0)); 344 | #6=DIRECTION('',(0.0,0.0,1.0)); 345 | #7=AXIS2_PLACEMENT_2D('',#5,#6); 346 | #8=CIRCLE('',#7,5.0); 347 | #9=EDGE_CURVE('',#2,#4,#8,.T.); 348 | #10=ORIENTED_EDGE('',*,*,#9,.T.); 349 | "); 350 | } 351 | 352 | [Fact] 353 | public void ReadEdgeLoopTest() 354 | { 355 | var edgeLoop = (StepEdgeLoop)ReadTopLevelItem(@" 356 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 357 | #2=DIRECTION('',(0.58,0.58,0.58)); 358 | #3=VECTOR('',#2,5.2); 359 | #4=LINE('',#1,#3); 360 | #5=EDGE_CURVE('',*,*,#4,.T.); 361 | #6=ORIENTED_EDGE('',*,*,#5,.T.); 362 | #7=CARTESIAN_POINT('',(7.0,8.0,9.0)); 363 | #8=VECTOR('',#2,5.2); 364 | #9=LINE('',#7,#8); 365 | #10=EDGE_CURVE('',*,*,#9,.F.); 366 | #11=EDGE_LOOP('',(#6,#12)); 367 | /* ensure that forward references are resolved when binding */ 368 | #12=ORIENTED_EDGE('',*,*,#10,.F.); 369 | "); 370 | Assert.Equal(2, edgeLoop.EdgeList.Count); 371 | Assert.NotNull(edgeLoop.EdgeList[0]); 372 | Assert.NotNull(edgeLoop.EdgeList[1]); 373 | } 374 | 375 | [Fact] 376 | public void ReadAdvancedFaceTest() 377 | { 378 | var file = ReadFile(@" 379 | #1=FACE_OUTER_BOUND('',#2,.T.); 380 | #2=EDGE_LOOP('',(#12,#13,#14,#15)); 381 | #3=LINE('',#31,#4); 382 | #4=VECTOR('',#24,2.5); 383 | #5=CIRCLE('',#18,2.5); 384 | #6=CIRCLE('',#19,2.5); 385 | #7=VERTEX_POINT('',#28); 386 | #8=VERTEX_POINT('',#30); 387 | #9=EDGE_CURVE('',#7,#7,#5,.T.); 388 | #10=EDGE_CURVE('',#7,#8,#3,.T.); 389 | #11=EDGE_CURVE('',#8,#8,#6,.T.); 390 | #12=ORIENTED_EDGE('',*,*,#9,.F.); 391 | #13=ORIENTED_EDGE('',*,*,#10,.T.); 392 | #14=ORIENTED_EDGE('',*,*,#11,.F.); 393 | #15=ORIENTED_EDGE('',*,*,#10,.F.); 394 | #16=CYLINDRICAL_SURFACE('',#17,2.5); 395 | #17=AXIS2_PLACEMENT_3D('',#27,#20,#21); 396 | #18=AXIS2_PLACEMENT_3D('',#29,#22,#23); 397 | #19=AXIS2_PLACEMENT_3D('',#32,#25,#26); 398 | #20=DIRECTION('center_axis',(0.,0.,-1.)); 399 | #21=DIRECTION('ref_axis',(-1.,0.,0.)); 400 | #22=DIRECTION('center_axis',(0.,0.,-1.)); 401 | #23=DIRECTION('ref_axis',(-1.,0.,0.)); 402 | #24=DIRECTION('',(0.,0.,-1.)); 403 | #25=DIRECTION('center_axis',(0.,0.,1.)); 404 | #26=DIRECTION('ref_axis',(-1.,0.,0.)); 405 | #27=CARTESIAN_POINT('Origin',(0.,0.,5.)); 406 | #28=CARTESIAN_POINT('',(2.5,3.06161699786838E-16,5.)); 407 | #29=CARTESIAN_POINT('Origin',(0.,0.,5.)); 408 | #30=CARTESIAN_POINT('',(2.5,3.06161699786838E-16,0.)); 409 | #31=CARTESIAN_POINT('',(2.5,-3.06161699786838E-16,5.)); 410 | #32=CARTESIAN_POINT('Origin',(0.,0.,0.)); 411 | #33=ADVANCED_FACE('',(#1),#16,.F.); 412 | "); 413 | var face = file.GetTopLevelItems().OfType().FirstOrDefault(); 414 | Assert.NotNull(face); 415 | Assert.NotNull(face.FaceGeometry); 416 | Assert.Single(face.Bounds); 417 | } 418 | 419 | [Fact] 420 | public void WriteEdgeLoopTest() 421 | { 422 | var edgeLoop = new StepEdgeLoop( 423 | "", 424 | new StepOrientedEdge("", null, null, new StepEdgeCurve("", null, null, StepLine.FromPoints(1.0, 2.0, 3.0, 4.0, 5.0, 6.0), true), true), 425 | new StepOrientedEdge("", null, null, new StepEdgeCurve("", null, null, StepLine.FromPoints(7.0, 8.0, 9.0, 10.0, 11.0, 12.0), false), false)); 426 | AssertFileContains(edgeLoop, @" 427 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 428 | #2=DIRECTION('',(0.58,0.58,0.58)); 429 | #3=VECTOR('',#2,5.2); 430 | #4=LINE('',#1,#3); 431 | #5=EDGE_CURVE('',*,*,#4,.T.); 432 | #6=ORIENTED_EDGE('',*,*,#5,.T.); 433 | #7=CARTESIAN_POINT('',(7.0,8.0,9.0)); 434 | #8=VECTOR('',#2,5.2); 435 | #9=LINE('',#7,#8); 436 | #10=EDGE_CURVE('',*,*,#9,.F.); 437 | #11=ORIENTED_EDGE('',*,*,#10,.F.); 438 | #12=EDGE_LOOP('',(#6,#11)); 439 | "); 440 | } 441 | 442 | [Fact] 443 | public void ReadFaceBoundTest() 444 | { 445 | var faceBound = (StepFaceBound)ReadTopLevelItem(@" 446 | #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); 447 | #2=DIRECTION('',(1.0,0.0,0.0)); 448 | #3=VECTOR('',#2,1.0); 449 | #4=LINE('',#1,#3); 450 | #5=EDGE_CURVE('',*,*,#4,.T.); 451 | #6=ORIENTED_EDGE('',*,*,#5,.T.); 452 | #7=EDGE_LOOP('',(#6)); 453 | #8=FACE_BOUND('',#7,.T.); 454 | "); 455 | Assert.True(faceBound.Orientation); 456 | } 457 | 458 | [Fact] 459 | public void WriteFaceBoundTest() 460 | { 461 | var faceBound = new StepFaceBound( 462 | "", 463 | new StepEdgeLoop( 464 | "", 465 | new StepOrientedEdge( 466 | "", 467 | null, 468 | null, 469 | new StepEdgeCurve( 470 | "", 471 | null, 472 | null, 473 | StepLine.FromPoints(0.0, 0.0, 0.0, 1.0, 0.0, 0.0), 474 | true), 475 | true)), 476 | true); 477 | AssertFileContains(faceBound, @" 478 | #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); 479 | #2=DIRECTION('',(1.0,0.0,0.0)); 480 | #3=VECTOR('',#2,1.0); 481 | #4=LINE('',#1,#3); 482 | #5=EDGE_CURVE('',*,*,#4,.T.); 483 | #6=ORIENTED_EDGE('',*,*,#5,.T.); 484 | #7=EDGE_LOOP('',(#6)); 485 | #8=FACE_BOUND('',#7,.T.); 486 | "); 487 | } 488 | 489 | [Fact] 490 | public void ReadFaceOuterBoundTest() 491 | { 492 | var faceOuterBound = (StepFaceOuterBound)ReadTopLevelItem(@" 493 | #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); 494 | #2=DIRECTION('',(1.0,0.0,0.0)); 495 | #3=VECTOR('',#2,1.0); 496 | #4=LINE('',#1,#3); 497 | #5=EDGE_CURVE('',*,*,#4,.T.); 498 | #6=ORIENTED_EDGE('',*,*,#5,.T.); 499 | #7=EDGE_LOOP('',(#6)); 500 | #8=FACE_OUTER_BOUND('',#7,.T.); 501 | "); 502 | Assert.True(faceOuterBound.Orientation); 503 | } 504 | 505 | [Fact] 506 | public void WriteFaceOuterBoundTest() 507 | { 508 | var faceOuterBound = new StepFaceOuterBound( 509 | "", 510 | new StepEdgeLoop( 511 | "", 512 | new StepOrientedEdge( 513 | "", 514 | null, 515 | null, 516 | new StepEdgeCurve( 517 | "", 518 | null, 519 | null, 520 | StepLine.FromPoints(0.0, 0.0, 0.0, 1.0, 0.0, 0.0), 521 | true), 522 | true)), 523 | true); 524 | AssertFileContains(faceOuterBound, @" 525 | #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); 526 | #2=DIRECTION('',(1.0,0.0,0.0)); 527 | #3=VECTOR('',#2,1.0); 528 | #4=LINE('',#1,#3); 529 | #5=EDGE_CURVE('',*,*,#4,.T.); 530 | #6=ORIENTED_EDGE('',*,*,#5,.T.); 531 | #7=EDGE_LOOP('',(#6)); 532 | #8=FACE_OUTER_BOUND('',#7,.T.); 533 | "); 534 | } 535 | 536 | [Fact] 537 | public void ReadCylindricalSurfaceTest() 538 | { 539 | var surface = (StepCylindricalSurface)ReadTopLevelItem(@" 540 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 541 | #2=DIRECTION('',(0.0,0.0,1.0)); 542 | #3=DIRECTION('',(1.0,0.0,0.0)); 543 | #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); 544 | #5=CYLINDRICAL_SURFACE('',#4,12.0); 545 | "); 546 | Assert.Equal(12.0, surface.Radius); 547 | } 548 | 549 | [Fact] 550 | public void WriteCylindricalSurfaceTest() 551 | { 552 | var surface = new StepCylindricalSurface( 553 | "", 554 | new StepAxis2Placement3D( 555 | "", 556 | new StepCartesianPoint("", 1.0, 2.0, 3.0), 557 | new StepDirection("", 0.0, 0.0, 1.0), 558 | new StepDirection("", 1.0, 0.0, 0.0)), 559 | 12.0); 560 | AssertFileContains(surface, @" 561 | #1=CARTESIAN_POINT('',(1.0,2.0,3.0)); 562 | #2=DIRECTION('',(0.0,0.0,1.0)); 563 | #3=DIRECTION('',(1.0,0.0,0.0)); 564 | #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); 565 | #5=CYLINDRICAL_SURFACE('',#4,12.0); 566 | "); 567 | } 568 | 569 | [Fact] 570 | public void WriteBSplineWithKnotsTest() 571 | { 572 | var spline = new StepBSplineCurveWithKnots( 573 | "", 574 | new StepCartesianPoint("", 0.0, 0.0, 0.0), 575 | new StepCartesianPoint("", 1.0, 0.0, 0.0), 576 | new StepCartesianPoint("", 1.0, 2.0, 0.0)); 577 | spline.KnotMultiplicities.Add(1); 578 | spline.Knots.Add(2.0); 579 | 580 | AssertFileContains(spline, @" 581 | #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); 582 | #2=CARTESIAN_POINT('',(1.0,0.0,0.0)); 583 | #3=CARTESIAN_POINT('',(1.0,2.0,0.0)); 584 | #4=B_SPLINE_CURVE_WITH_KNOTS('',0,(#1,#2,#3),.UNSPECIFIED.,.F.,.F.,(1),(2.0), 585 | .UNSPECIFIED.); 586 | "); 587 | } 588 | } 589 | } 590 | -------------------------------------------------------------------------------- /src/IxMilia.Step.Test/StepTestBase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Xunit; 3 | 4 | namespace IxMilia.Step.Test 5 | { 6 | public abstract class StepTestBase 7 | { 8 | protected static string NormalizeLineEndings(string str) 9 | { 10 | var lines = str.Split('\n').Select(l => l.TrimEnd('\r')); 11 | return string.Join("\r\n", lines); 12 | } 13 | 14 | protected void AssertFileIs(StepFile file, string expected, bool inlineReferences = false) 15 | { 16 | var actual = file.GetContentsAsString(inlineReferences); 17 | var expectedNormalizedLines = NormalizeLineEndings(expected); 18 | Assert.Equal(expectedNormalizedLines, actual); 19 | } 20 | 21 | protected void AssertFileContains(StepFile file, string expected, bool inlineReferences = false) 22 | { 23 | var actual = file.GetContentsAsString(inlineReferences); 24 | var expectedNormalizedLines = NormalizeLineEndings(expected); 25 | Assert.Contains(expectedNormalizedLines, actual); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IxMilia.Step.Test/StepTokenTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using IxMilia.Step.Tokens; 8 | using Xunit; 9 | 10 | namespace IxMilia.Step.Test 11 | { 12 | public class StepTokenTests : StepTestBase 13 | { 14 | private StepToken[] GetTokens(string text) 15 | { 16 | using (var stream = new MemoryStream()) 17 | using (var writer = new StreamWriter(stream)) 18 | { 19 | writer.Write(text); 20 | writer.Flush(); 21 | stream.Seek(0, SeekOrigin.Begin); 22 | var tokenizer = new StepTokenizer(stream); 23 | return tokenizer.GetTokens().ToArray(); 24 | } 25 | } 26 | 27 | delegate bool TokenVerifier(StepToken token); 28 | 29 | private TokenVerifier WithKind(StepTokenKind kind) 30 | { 31 | return token => token.Kind == kind; 32 | } 33 | 34 | private TokenVerifier Semicolon() 35 | { 36 | return WithKind(StepTokenKind.Semicolon); 37 | } 38 | 39 | private TokenVerifier Keyword(string keyword) 40 | { 41 | return token => WithKind(StepTokenKind.Keyword)(token) && ((StepKeywordToken)token).Value == keyword; 42 | } 43 | 44 | private TokenVerifier Enumeration(string enumName) 45 | { 46 | return token => WithKind(StepTokenKind.Enumeration)(token) && ((StepEnumerationToken)token).Value == enumName; 47 | } 48 | 49 | private TokenVerifier Real(double value) 50 | { 51 | return token => WithKind(StepTokenKind.Real)(token) && ((StepRealToken)token).Value == value; 52 | } 53 | 54 | private TokenVerifier Integer(int value) 55 | { 56 | return token => WithKind(StepTokenKind.Integer)(token) && ((StepIntegerToken)token).Value == value; 57 | } 58 | 59 | private void VerifyTokens(string text, params TokenVerifier[] expected) 60 | { 61 | var actual = GetTokens(text); 62 | var upper = Math.Min(actual.Length, expected.Length); 63 | for (int i = 0; i < upper; i++) 64 | { 65 | Assert.True(expected[i](actual[i])); 66 | } 67 | } 68 | 69 | [Fact] 70 | public void SpecialTokenTest() 71 | { 72 | VerifyTokens("ISO-10303-21;", 73 | Keyword("ISO-10303-21"), Semicolon()); 74 | } 75 | 76 | [Fact] 77 | public void EmptyFileTest() 78 | { 79 | VerifyTokens(@" 80 | ISO-10303-21; 81 | HEADER; 82 | ENDSEC; 83 | DATA; 84 | ENDSEC; 85 | END-ISO-10303-21;", 86 | Keyword("ISO-10303-21"), Semicolon(), 87 | Keyword("HEADER"), Semicolon(), 88 | Keyword("ENDSEC"), Semicolon(), 89 | Keyword("DATA"), Semicolon(), 90 | Keyword("ENDSEC"), Semicolon(), 91 | Keyword("END-ISO-10303-21"), Semicolon()); 92 | } 93 | 94 | [Fact] 95 | public void TokensWithCommentsTest1() 96 | { 97 | VerifyTokens(@" 98 | HEADER; 99 | /* comment DATA; (comment's end is on the same line) */ 100 | ENDSEC;", 101 | Keyword("HEADER"), Semicolon(), 102 | Keyword("ENDSEC"), Semicolon()); 103 | } 104 | 105 | [Fact] 106 | public void TokensWithCommentsTest2() 107 | { 108 | VerifyTokens(@" 109 | HEADER; 110 | /* comment (comment's end is on another line) 111 | DATA; */ 112 | ENDSEC;", 113 | Keyword("HEADER"), Semicolon(), 114 | Keyword("ENDSEC"), Semicolon()); 115 | } 116 | 117 | [Fact] 118 | public void TokensWithCommentsTest3() 119 | { 120 | VerifyTokens(@" 121 | HEADER; 122 | /* comment 123 | (comment's end is on another line and farther back) 124 | DATA; */ 125 | ENDSEC;", 126 | Keyword("HEADER"), Semicolon(), 127 | Keyword("ENDSEC"), Semicolon()); 128 | } 129 | 130 | [Fact] 131 | public void TokensWithEmptylines() 132 | { 133 | VerifyTokens(@" 134 | 135 | HEADER; 136 | 137 | ENDSEC; 138 | 139 | ", 140 | Keyword("HEADER"), Semicolon(), 141 | Keyword("ENDSEC"), Semicolon()); 142 | } 143 | 144 | [Fact] 145 | public void ParseEnumTokensTest() 146 | { 147 | VerifyTokens(".SOME_ENUM_VALUE.", Enumeration("SOME_ENUM_VALUE")); 148 | } 149 | 150 | [Fact] 151 | public void ParseInvarianCultureTest() 152 | { 153 | var existingCulture = CultureInfo.CurrentCulture; 154 | try 155 | { 156 | CultureInfo.CurrentCulture = new CultureInfo("de-DE"); 157 | VerifyTokens("1.8", Real(1.8)); 158 | VerifyTokens("54", Integer(54)); 159 | } 160 | finally 161 | { 162 | CultureInfo.CurrentCulture = existingCulture; 163 | } 164 | } 165 | 166 | [Fact] 167 | public void WriteTokensPastLineLengthTest() 168 | { 169 | var tokens = new List(); 170 | var maxItems = 22; 171 | for (int i = 0; i < maxItems; i++) 172 | { 173 | tokens.Add(new StepRealToken(0.0, -1, -1)); 174 | if (i < maxItems - 1) 175 | { 176 | tokens.Add(StepCommaToken.Instance); 177 | } 178 | } 179 | 180 | // should wrap at 80 characters 181 | var writer = new StepWriter(null, false); 182 | var sb = new StringBuilder(); 183 | writer.WriteTokens(tokens, sb); 184 | var expected = NormalizeLineEndings(@" 185 | 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0, 186 | 0.0,0.0 187 | ".Trim()); 188 | Assert.Equal(expected, sb.ToString()); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepAdvancedFace.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public class StepAdvancedFace : StepFaceSurface 7 | { 8 | public StepAdvancedFace(string name) 9 | : base(name) 10 | { 11 | } 12 | 13 | private StepAdvancedFace() 14 | : base(string.Empty) 15 | { 16 | } 17 | 18 | public override StepItemType ItemType => StepItemType.AdvancedFace; 19 | 20 | internal static StepAdvancedFace CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 21 | { 22 | var face = new StepAdvancedFace(); 23 | syntaxList.AssertListCount(4); 24 | face.Name = syntaxList.Values[0].GetStringValue(); 25 | 26 | var boundsList = syntaxList.Values[1].GetValueList(); 27 | face.Bounds.Clear(); 28 | face.Bounds.AddRange(Enumerable.Range(0, boundsList.Values.Count).Select(_ => (StepFaceBound)null)); 29 | for (int i = 0; i < boundsList.Values.Count; i++) 30 | { 31 | var j = i; // capture to avoid rebinding 32 | binder.BindValue(boundsList.Values[j], v => face.Bounds[j] = v.AsType()); 33 | } 34 | binder.BindValue(syntaxList.Values[2], v => face.FaceGeometry = v.AsType()); 35 | face.SameSense = syntaxList.Values[3].GetBooleanValue(); 36 | 37 | return face; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepAxis2Placement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IxMilia.Step.Items 4 | { 5 | public abstract class StepAxis2Placement : StepPlacement 6 | { 7 | private StepCartesianPoint _location; 8 | private StepDirection _refDirection; 9 | 10 | public StepCartesianPoint Location 11 | { 12 | get { return _location; } 13 | set 14 | { 15 | if (value == null) 16 | { 17 | throw new ArgumentNullException(); 18 | } 19 | 20 | _location = value; 21 | } 22 | } 23 | 24 | public StepDirection RefDirection 25 | { 26 | get { return _refDirection; } 27 | set 28 | { 29 | if (value == null) 30 | { 31 | throw new ArgumentNullException(); 32 | } 33 | 34 | _refDirection = value; 35 | } 36 | } 37 | 38 | protected StepAxis2Placement(string name) 39 | : base(name) 40 | { 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepAxis2Placement2D.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public class StepAxis2Placement2D : StepAxis2Placement 7 | { 8 | public override StepItemType ItemType => StepItemType.AxisPlacement2D; 9 | 10 | private StepAxis2Placement2D() 11 | : base(string.Empty) 12 | { 13 | } 14 | 15 | public StepAxis2Placement2D(string label, StepCartesianPoint location, StepDirection direction) 16 | : base(label) 17 | { 18 | Location = location; 19 | RefDirection = direction; 20 | } 21 | 22 | internal override IEnumerable GetReferencedItems() 23 | { 24 | yield return Location; 25 | yield return RefDirection; 26 | } 27 | 28 | internal override IEnumerable GetParameters(StepWriter writer) 29 | { 30 | foreach (var parameter in base.GetParameters(writer)) 31 | { 32 | yield return parameter; 33 | } 34 | 35 | yield return writer.GetItemSyntax(Location); 36 | yield return writer.GetItemSyntax(RefDirection); 37 | } 38 | 39 | internal static StepAxis2Placement2D CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 40 | { 41 | var axis = new StepAxis2Placement2D(); 42 | syntaxList.AssertListCount(3); 43 | axis.Name = syntaxList.Values[0].GetStringValue(); 44 | binder.BindValue(syntaxList.Values[1], v => axis.Location = v.AsType()); 45 | binder.BindValue(syntaxList.Values[2], v => axis.RefDirection = v.AsType()); 46 | return axis; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepAxis2Placement3D.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepAxis2Placement3D : StepAxis2Placement 8 | { 9 | public override StepItemType ItemType => StepItemType.AxisPlacement3D; 10 | 11 | private StepDirection _axis; 12 | 13 | public StepDirection Axis 14 | { 15 | get { return _axis; } 16 | set 17 | { 18 | if (value == null) 19 | { 20 | throw new ArgumentNullException(); 21 | } 22 | 23 | _axis = value; 24 | } 25 | } 26 | 27 | private StepAxis2Placement3D() 28 | : base(string.Empty) 29 | { 30 | } 31 | 32 | public StepAxis2Placement3D(string name, StepCartesianPoint location, StepDirection axis, StepDirection refDirection) 33 | : base(name) 34 | { 35 | Location = location; 36 | Axis = axis; 37 | RefDirection = refDirection; 38 | } 39 | 40 | internal override IEnumerable GetReferencedItems() 41 | { 42 | yield return Location; 43 | yield return Axis; 44 | yield return RefDirection; 45 | } 46 | 47 | internal override IEnumerable GetParameters(StepWriter writer) 48 | { 49 | foreach (var parameter in base.GetParameters(writer)) 50 | { 51 | yield return parameter; 52 | } 53 | 54 | yield return writer.GetItemSyntax(Location); 55 | yield return writer.GetItemSyntax(Axis); 56 | yield return writer.GetItemSyntax(RefDirection); 57 | } 58 | 59 | internal static StepAxis2Placement3D CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 60 | { 61 | var axis = new StepAxis2Placement3D(); 62 | syntaxList.AssertListCount(4); 63 | axis.Name = syntaxList.Values[0].GetStringValue(); 64 | binder.BindValue(syntaxList.Values[1], v => axis.Location = v.AsType()); 65 | binder.BindValue(syntaxList.Values[2], v => axis.Axis = v.AsType()); 66 | binder.BindValue(syntaxList.Values[3], v => axis.RefDirection = v.AsType()); 67 | return axis; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepBSplineCurve.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IxMilia.Step.Syntax; 5 | 6 | namespace IxMilia.Step.Items 7 | { 8 | public enum StepBSplineCurveForm 9 | { 10 | Polyline, 11 | CircularArc, 12 | EllipticalArc, 13 | ParabolicArc, 14 | HyperbolicArc, 15 | Unspecified 16 | }; 17 | 18 | public abstract class StepBSplineCurve : StepBoundedCurve 19 | { 20 | public int Degree { get; set; } 21 | 22 | public List ControlPointsList { get; } = new List(); 23 | 24 | public StepBSplineCurveForm CurveForm { get; set; } = StepBSplineCurveForm.Unspecified; 25 | 26 | public bool ClosedCurve { get; set; } 27 | 28 | public bool SelfIntersect { get; set; } 29 | 30 | public StepBSplineCurve(string name, IEnumerable controlPoints) : base(name) 31 | { 32 | ControlPointsList.AddRange(controlPoints); 33 | } 34 | 35 | public StepBSplineCurve(string name, params StepCartesianPoint[] controlPoints) 36 | : this(name, (IEnumerable)controlPoints) 37 | { 38 | } 39 | 40 | internal override IEnumerable GetReferencedItems() 41 | { 42 | return ControlPointsList; 43 | } 44 | 45 | internal override IEnumerable GetParameters(StepWriter writer) 46 | { 47 | foreach (var parameter in base.GetParameters(writer)) 48 | { 49 | yield return parameter; 50 | } 51 | 52 | yield return new StepIntegerSyntax(Degree); 53 | yield return new StepSyntaxList(ControlPointsList.Select(c => writer.GetItemSyntax(c))); 54 | yield return new StepEnumerationValueSyntax(GetCurveFormString(CurveForm)); 55 | yield return StepWriter.GetBooleanSyntax(ClosedCurve); 56 | yield return StepWriter.GetBooleanSyntax(SelfIntersect); 57 | } 58 | 59 | private const string POLYLINE_FORM = "POLYLINE_FORM"; 60 | private const string CIRCULAR_ARC = "CIRCULAR_ARC"; 61 | private const string ELLIPTIC_ARC = "ELLIPTIC_ARC"; 62 | private const string PARABOLIC_ARC = "PARABOLIC_ARC"; 63 | private const string HYPERBOLIC_ARC = "HYPERBOLIC_ARC"; 64 | private const string UNSPECIFIED = "UNSPECIFIED"; 65 | 66 | protected static StepBSplineCurveForm ParseCurveForm(string enumerationValue) 67 | { 68 | switch (enumerationValue.ToUpperInvariant()) 69 | { 70 | case POLYLINE_FORM: 71 | return StepBSplineCurveForm.Polyline; 72 | case CIRCULAR_ARC: 73 | return StepBSplineCurveForm.CircularArc; 74 | case ELLIPTIC_ARC: 75 | return StepBSplineCurveForm.EllipticalArc; 76 | case PARABOLIC_ARC: 77 | return StepBSplineCurveForm.ParabolicArc; 78 | case HYPERBOLIC_ARC: 79 | return StepBSplineCurveForm.HyperbolicArc; 80 | default: 81 | return StepBSplineCurveForm.Unspecified; 82 | } 83 | } 84 | 85 | protected static string GetCurveFormString(StepBSplineCurveForm form) 86 | { 87 | switch (form) 88 | { 89 | case StepBSplineCurveForm.Polyline: 90 | return POLYLINE_FORM; 91 | case StepBSplineCurveForm.CircularArc: 92 | return CIRCULAR_ARC; 93 | case StepBSplineCurveForm.EllipticalArc: 94 | return ELLIPTIC_ARC; 95 | case StepBSplineCurveForm.ParabolicArc: 96 | return PARABOLIC_ARC; 97 | case StepBSplineCurveForm.HyperbolicArc: 98 | return HYPERBOLIC_ARC; 99 | case StepBSplineCurveForm.Unspecified: 100 | return UNSPECIFIED; 101 | } 102 | 103 | throw new NotImplementedException(); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepBSplineCurveWithKnots.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IxMilia.Step.Syntax; 5 | 6 | namespace IxMilia.Step.Items 7 | { 8 | public enum StepKnotType 9 | { 10 | UniformKnots, 11 | QuasiUniformKnots, 12 | PiecewiseBezierKnots, 13 | Unspecified 14 | }; 15 | 16 | public class StepBSplineCurveWithKnots : StepBSplineCurve 17 | { 18 | public List KnotMultiplicities { get; } = new List(); 19 | 20 | public List Knots { get; } = new List(); 21 | 22 | public StepKnotType KnotSpec { get; set; } = StepKnotType.Unspecified; 23 | 24 | public StepBSplineCurveWithKnots(string name, IEnumerable controlPoints) 25 | : base(name, controlPoints) 26 | { 27 | } 28 | 29 | public StepBSplineCurveWithKnots(string name, params StepCartesianPoint[] controlPoints) 30 | : base(name, controlPoints) 31 | { 32 | } 33 | 34 | public override StepItemType ItemType => StepItemType.BSplineCurveWithKnots; 35 | 36 | private const string UNIFORM_KNOTS = "UNIFORM_KNOTS"; 37 | private const string QUASI_UNIFORM_KNOTS = "QUASI_UNIFORM_KNOTS"; 38 | private const string PIECEWISE_BEZIER_KNOTS = "PIECEWISE_BEZIER_KNOTS"; 39 | private const string UNSPECIFIED = "UNSPECIFIED"; 40 | 41 | private static StepKnotType ParseKnotSpec(string enumerationValue) 42 | { 43 | switch (enumerationValue.ToUpperInvariant()) 44 | { 45 | case UNIFORM_KNOTS: 46 | return StepKnotType.UniformKnots; 47 | case QUASI_UNIFORM_KNOTS: 48 | return StepKnotType.QuasiUniformKnots; 49 | case PIECEWISE_BEZIER_KNOTS: 50 | return StepKnotType.PiecewiseBezierKnots; 51 | default: 52 | return StepKnotType.Unspecified; 53 | } 54 | } 55 | 56 | private static string GetKnotSpec(StepKnotType spec) 57 | { 58 | switch (spec) 59 | { 60 | case StepKnotType.UniformKnots: 61 | return UNIFORM_KNOTS; 62 | case StepKnotType.QuasiUniformKnots: 63 | return QUASI_UNIFORM_KNOTS; 64 | case StepKnotType.PiecewiseBezierKnots: 65 | return PIECEWISE_BEZIER_KNOTS; 66 | case StepKnotType.Unspecified: 67 | return UNSPECIFIED; 68 | } 69 | 70 | throw new NotImplementedException(); 71 | } 72 | 73 | internal override IEnumerable GetParameters(StepWriter writer) 74 | { 75 | foreach (var parameter in base.GetParameters(writer)) 76 | { 77 | yield return parameter; 78 | } 79 | 80 | yield return new StepSyntaxList(KnotMultiplicities.Select(m => new StepIntegerSyntax(m))); 81 | yield return new StepSyntaxList(Knots.Select(k => new StepRealSyntax(k))); 82 | yield return new StepEnumerationValueSyntax(GetKnotSpec(KnotSpec)); 83 | } 84 | 85 | internal static StepBSplineCurveWithKnots CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 86 | { 87 | syntaxList.AssertListCount(9); 88 | var controlPointsList = syntaxList.Values[2].GetValueList(); 89 | 90 | var spline = new StepBSplineCurveWithKnots(string.Empty, new StepCartesianPoint[controlPointsList.Values.Count]); 91 | spline.Name = syntaxList.Values[0].GetStringValue(); 92 | spline.Degree = syntaxList.Values[1].GetIntegerValue(); 93 | 94 | for (int i = 0; i < controlPointsList.Values.Count; i++) 95 | { 96 | var j = i; // capture to avoid rebinding 97 | binder.BindValue(controlPointsList.Values[j], v => spline.ControlPointsList[j] = v.AsType()); 98 | } 99 | 100 | spline.CurveForm = ParseCurveForm(syntaxList.Values[3].GetEnumerationValue()); 101 | spline.ClosedCurve = syntaxList.Values[4].GetBooleanValue(); 102 | spline.SelfIntersect = syntaxList.Values[5].GetBooleanValue(); 103 | 104 | var knotMultiplicitiesList = syntaxList.Values[6].GetValueList(); 105 | spline.KnotMultiplicities.Clear(); 106 | for (int i = 0; i < knotMultiplicitiesList.Values.Count; i++) 107 | { 108 | spline.KnotMultiplicities.Add(knotMultiplicitiesList.Values[i].GetIntegerValue()); 109 | } 110 | 111 | var knotslist = syntaxList.Values[7].GetValueList(); 112 | spline.Knots.Clear(); 113 | for (int i = 0; i < knotslist.Values.Count; i++) 114 | { 115 | spline.Knots.Add(knotslist.Values[i].GetRealVavlue()); 116 | } 117 | 118 | spline.KnotSpec = ParseKnotSpec(syntaxList.Values[8].GetEnumerationValue()); 119 | 120 | return spline; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepBoundedCurve.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepBoundedCurve : StepCurve 4 | { 5 | protected StepBoundedCurve(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepCartesianPoint.cs: -------------------------------------------------------------------------------- 1 | using IxMilia.Step.Syntax; 2 | 3 | namespace IxMilia.Step.Items 4 | { 5 | public class StepCartesianPoint : StepTriple 6 | { 7 | public override StepItemType ItemType => StepItemType.CartesianPoint; 8 | protected override int MinimumValueCount => 1; 9 | 10 | private StepCartesianPoint() 11 | { 12 | } 13 | 14 | public StepCartesianPoint(string label, double x, double y, double z) 15 | : base(label, x, y, z) 16 | { 17 | } 18 | 19 | internal static StepCartesianPoint CreateFromSyntaxList(StepSyntaxList syntaxList) 20 | { 21 | return (StepCartesianPoint)AssignTo(new StepCartesianPoint(), syntaxList); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepCircle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepCircle : StepConic 8 | { 9 | public override StepItemType ItemType => StepItemType.Circle; 10 | 11 | private StepAxis2Placement _position; 12 | 13 | public StepAxis2Placement Position 14 | { 15 | get { return _position; } 16 | set 17 | { 18 | if (value == null) 19 | { 20 | throw new ArgumentNullException(); 21 | } 22 | 23 | _position = value; 24 | } 25 | } 26 | 27 | public double Radius { get; set; } 28 | 29 | private StepCircle() 30 | : base(string.Empty) 31 | { 32 | } 33 | 34 | public StepCircle(string label, StepAxis2Placement position, double radius) 35 | : base(label) 36 | { 37 | Position = position; 38 | Radius = radius; 39 | } 40 | 41 | internal override IEnumerable GetReferencedItems() 42 | { 43 | yield return Position; 44 | } 45 | 46 | internal override IEnumerable GetParameters(StepWriter writer) 47 | { 48 | foreach (var parameter in base.GetParameters(writer)) 49 | { 50 | yield return parameter; 51 | } 52 | 53 | yield return writer.GetItemSyntax(Position); 54 | yield return new StepRealSyntax(Radius); 55 | } 56 | 57 | internal static StepCircle CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 58 | { 59 | var circle = new StepCircle(); 60 | syntaxList.AssertListCount(3); 61 | circle.Name = syntaxList.Values[0].GetStringValue(); 62 | binder.BindValue(syntaxList.Values[1], v => circle.Position = v.AsType()); 63 | circle.Radius = syntaxList.Values[2].GetRealVavlue(); 64 | return circle; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepConic.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepConic : StepCurve 4 | { 5 | protected StepConic(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepCurve.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepCurve : StepGeometricRepresentationItem 4 | { 5 | protected StepCurve(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepCylindricalSurface.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public class StepCylindricalSurface : StepElementarySurface 7 | { 8 | public override StepItemType ItemType => StepItemType.CylindricalSurface; 9 | 10 | public double Radius { get; set; } 11 | 12 | private StepCylindricalSurface() 13 | : base() 14 | { 15 | } 16 | 17 | public StepCylindricalSurface(string name, StepAxis2Placement3D position, double radius) 18 | : base(name, position) 19 | { 20 | Radius = radius; 21 | } 22 | 23 | internal override IEnumerable GetParameters(StepWriter writer) 24 | { 25 | foreach (var parameter in base.GetParameters(writer)) 26 | { 27 | yield return parameter; 28 | } 29 | 30 | yield return new StepRealSyntax(Radius); 31 | } 32 | 33 | internal static StepRepresentationItem CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 34 | { 35 | syntaxList.AssertListCount(3); 36 | var surface = new StepCylindricalSurface(); 37 | surface.Name = syntaxList.Values[0].GetStringValue(); 38 | binder.BindValue(syntaxList.Values[1], v => surface.Position = v.AsType()); 39 | surface.Radius = syntaxList.Values[2].GetRealVavlue(); 40 | return surface; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepDirection.cs: -------------------------------------------------------------------------------- 1 | using IxMilia.Step.Syntax; 2 | 3 | namespace IxMilia.Step.Items 4 | { 5 | public class StepDirection : StepTriple 6 | { 7 | public override StepItemType ItemType => StepItemType.Direction; 8 | protected override int MinimumValueCount => 2; 9 | 10 | private StepDirection() 11 | { 12 | } 13 | 14 | public StepDirection(string name, double x, double y, double z) 15 | : base(name, x, y, z) 16 | { 17 | } 18 | 19 | internal static StepDirection CreateFromSyntaxList(StepSyntaxList syntaxList) 20 | { 21 | return (StepDirection)AssignTo(new StepDirection(), syntaxList); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepEdge.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public abstract class StepEdge : StepTopologicalRepresentationItem 7 | { 8 | public StepVertex EdgeStart { get; set; } 9 | public StepVertex EdgeEnd { get; set; } 10 | 11 | protected StepEdge() 12 | : base(string.Empty) 13 | { 14 | } 15 | 16 | protected StepEdge(string name, StepVertex edgeStart, StepVertex edgeEnd) 17 | : base(name) 18 | { 19 | EdgeStart = edgeStart; 20 | EdgeEnd = edgeEnd; 21 | } 22 | 23 | internal override IEnumerable GetReferencedItems() 24 | { 25 | if (EdgeStart != null) 26 | { 27 | yield return EdgeStart; 28 | } 29 | 30 | if (EdgeEnd != null) 31 | { 32 | yield return EdgeEnd; 33 | } 34 | } 35 | 36 | internal override IEnumerable GetParameters(StepWriter writer) 37 | { 38 | foreach (var parameter in base.GetParameters(writer)) 39 | { 40 | yield return parameter; 41 | } 42 | 43 | yield return writer.GetItemSyntaxOrAuto(EdgeStart); 44 | yield return writer.GetItemSyntaxOrAuto(EdgeEnd); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepEdgeCurve.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepEdgeCurve : StepEdge 8 | { 9 | public override StepItemType ItemType => StepItemType.EdgeCurve; 10 | 11 | private StepCurve _edgeGeometry; 12 | 13 | public StepCurve EdgeGeometry 14 | { 15 | get { return _edgeGeometry; } 16 | set 17 | { 18 | if (value == null) 19 | { 20 | throw new ArgumentNullException(); 21 | } 22 | 23 | _edgeGeometry = value; 24 | } 25 | } 26 | 27 | public bool IsSameSense { get; set; } 28 | 29 | private StepEdgeCurve() 30 | : base() 31 | { 32 | } 33 | 34 | public StepEdgeCurve(string name, StepVertex edgeStart, StepVertex edgeEnd, StepCurve edgeGeometry, bool isSameSense) 35 | : base(name, edgeStart, edgeEnd) 36 | { 37 | EdgeGeometry = edgeGeometry; 38 | IsSameSense = isSameSense; 39 | } 40 | 41 | internal override IEnumerable GetReferencedItems() 42 | { 43 | foreach (var item in base.GetReferencedItems()) 44 | { 45 | yield return item; 46 | } 47 | 48 | yield return EdgeGeometry; 49 | } 50 | 51 | internal override IEnumerable GetParameters(StepWriter writer) 52 | { 53 | foreach (var parameter in base.GetParameters(writer)) 54 | { 55 | yield return parameter; 56 | } 57 | 58 | yield return writer.GetItemSyntax(EdgeGeometry); 59 | yield return StepWriter.GetBooleanSyntax(IsSameSense); 60 | } 61 | 62 | internal static StepEdgeCurve CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 63 | { 64 | var edgeCurve = new StepEdgeCurve(); 65 | syntaxList.AssertListCount(5); 66 | edgeCurve.Name = syntaxList.Values[0].GetStringValue(); 67 | binder.BindValue(syntaxList.Values[1], v => edgeCurve.EdgeStart = v.AsType()); 68 | binder.BindValue(syntaxList.Values[2], v => edgeCurve.EdgeEnd = v.AsType()); 69 | binder.BindValue(syntaxList.Values[3], v => edgeCurve.EdgeGeometry = v.AsType()); 70 | edgeCurve.IsSameSense = syntaxList.Values[4].GetBooleanValue(); 71 | return edgeCurve; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepEdgeLoop.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepEdgeLoop : StepLoop 8 | { 9 | public override StepItemType ItemType => StepItemType.EdgeLoop; 10 | 11 | public List EdgeList { get; private set; } 12 | 13 | public StepEdgeLoop(string name, IEnumerable edgeList) 14 | : base(name) 15 | { 16 | EdgeList = new List(edgeList); 17 | } 18 | 19 | public StepEdgeLoop(string name, params StepOrientedEdge[] edgeList) 20 | : this(name, (IEnumerable)edgeList) 21 | { 22 | } 23 | 24 | internal override IEnumerable GetReferencedItems() 25 | { 26 | return EdgeList; 27 | } 28 | 29 | internal override IEnumerable GetParameters(StepWriter writer) 30 | { 31 | foreach (var parameter in base.GetParameters(writer)) 32 | { 33 | yield return parameter; 34 | } 35 | 36 | yield return new StepSyntaxList(-1, -1, EdgeList.Select(e => writer.GetItemSyntax(e))); 37 | } 38 | 39 | internal static StepEdgeLoop CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 40 | { 41 | syntaxList.AssertListCount(2); 42 | var edgeSyntaxList = syntaxList.Values[1].GetValueList(); 43 | var edgeLoop = new StepEdgeLoop(string.Empty, new StepOrientedEdge[edgeSyntaxList.Values.Count]); 44 | edgeLoop.Name = syntaxList.Values[0].GetStringValue(); 45 | for (int i = 0; i < edgeSyntaxList.Values.Count; i++) 46 | { 47 | var j = i; // capture to avoid rebinding 48 | binder.BindValue(edgeSyntaxList.Values[j], v => edgeLoop.EdgeList[j] = v.AsType()); 49 | } 50 | 51 | return edgeLoop; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepElementarySurface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public abstract class StepElementarySurface : StepSurface 8 | { 9 | private StepAxis2Placement3D _position; 10 | 11 | public StepAxis2Placement3D Position 12 | { 13 | get { return _position; } 14 | set 15 | { 16 | if (value == null) 17 | { 18 | throw new ArgumentNullException(); 19 | } 20 | 21 | _position = value; 22 | } 23 | } 24 | 25 | protected StepElementarySurface() 26 | : base(string.Empty) 27 | { 28 | } 29 | 30 | public StepElementarySurface(string name, StepAxis2Placement3D position) 31 | : base(name) 32 | { 33 | Position = position; 34 | } 35 | 36 | internal override IEnumerable GetReferencedItems() 37 | { 38 | yield return Position; 39 | } 40 | 41 | internal override IEnumerable GetParameters(StepWriter writer) 42 | { 43 | foreach (var parameter in base.GetParameters(writer)) 44 | { 45 | yield return parameter; 46 | } 47 | 48 | yield return writer.GetItemSyntax(Position); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepEllipse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepEllipse : StepConic 8 | { 9 | public override StepItemType ItemType => StepItemType.Ellipse; 10 | 11 | private StepAxis2Placement _position; 12 | 13 | public StepAxis2Placement Position 14 | { 15 | get { return _position; } 16 | set 17 | { 18 | if (value == null) 19 | { 20 | throw new ArgumentNullException(); 21 | } 22 | 23 | _position = value; 24 | } 25 | } 26 | 27 | public double SemiAxis1 { get; set; } 28 | public double SemiAxis2 { get; set; } 29 | 30 | private StepEllipse() 31 | : base(string.Empty) 32 | { 33 | } 34 | 35 | public StepEllipse(string name, StepAxis2Placement position, double semiAxis1, double semiAxis2) 36 | : base(name) 37 | { 38 | Position = position; 39 | SemiAxis1 = semiAxis1; 40 | SemiAxis2 = semiAxis2; 41 | } 42 | 43 | internal override IEnumerable GetReferencedItems() 44 | { 45 | yield return Position; 46 | } 47 | 48 | internal override IEnumerable GetParameters(StepWriter writer) 49 | { 50 | foreach (var parameter in base.GetParameters(writer)) 51 | { 52 | yield return parameter; 53 | } 54 | 55 | yield return writer.GetItemSyntax(Position); 56 | yield return new StepRealSyntax(SemiAxis1); 57 | yield return new StepRealSyntax(SemiAxis2); 58 | } 59 | 60 | internal static StepEllipse CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 61 | { 62 | var ellipse = new StepEllipse(); 63 | syntaxList.AssertListCount(4); 64 | ellipse.Name = syntaxList.Values[0].GetStringValue(); 65 | binder.BindValue(syntaxList.Values[1], v => ellipse.Position = v.AsType()); 66 | ellipse.SemiAxis1 = syntaxList.Values[2].GetRealVavlue(); 67 | ellipse.SemiAxis2 = syntaxList.Values[3].GetRealVavlue(); 68 | return ellipse; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepFace.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public abstract class StepFace : StepTopologicalRepresentationItem 8 | { 9 | public List Bounds { get; } = new List(); 10 | 11 | public StepFace(string name) 12 | : base(name) 13 | { 14 | } 15 | 16 | internal override IEnumerable GetParameters(StepWriter writer) 17 | { 18 | foreach (var parameter in base.GetParameters(writer)) 19 | { 20 | yield return parameter; 21 | } 22 | 23 | yield return new StepSyntaxList(Bounds.Select(b => writer.GetItemSyntax(b))); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepFaceBound.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public class StepFaceBound : StepTopologicalRepresentationItem 7 | { 8 | public override StepItemType ItemType => StepItemType.FaceBound; 9 | 10 | public StepLoop Bound { get; set; } 11 | public bool Orientation { get; set; } 12 | 13 | protected StepFaceBound() 14 | : base(string.Empty) 15 | { 16 | } 17 | 18 | public StepFaceBound(string name, StepLoop bound, bool orientation) 19 | : base(name) 20 | { 21 | Bound = bound; 22 | Orientation = orientation; 23 | } 24 | 25 | internal override IEnumerable GetReferencedItems() 26 | { 27 | yield return Bound; 28 | } 29 | 30 | internal override IEnumerable GetParameters(StepWriter writer) 31 | { 32 | foreach (var parameter in base.GetParameters(writer)) 33 | { 34 | yield return parameter; 35 | } 36 | 37 | yield return writer.GetItemSyntax(Bound); 38 | yield return StepWriter.GetBooleanSyntax(Orientation); 39 | } 40 | 41 | internal static StepFaceBound CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 42 | { 43 | syntaxList.AssertListCount(3); 44 | var faceBound = new StepFaceBound(); 45 | faceBound.Name = syntaxList.Values[0].GetStringValue(); 46 | binder.BindValue(syntaxList.Values[1], v => faceBound.Bound = v.AsType()); 47 | faceBound.Orientation = syntaxList.Values[2].GetBooleanValue(); 48 | return faceBound; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepFaceOuterBound.cs: -------------------------------------------------------------------------------- 1 | using IxMilia.Step.Syntax; 2 | 3 | namespace IxMilia.Step.Items 4 | { 5 | public class StepFaceOuterBound : StepFaceBound 6 | { 7 | public override StepItemType ItemType => StepItemType.FaceOuterBound; 8 | 9 | private StepFaceOuterBound() 10 | : base() 11 | { 12 | } 13 | 14 | public StepFaceOuterBound(string name, StepLoop bound, bool orientation) 15 | : base(name, bound, orientation) 16 | { 17 | } 18 | 19 | internal static new StepFaceOuterBound CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 20 | { 21 | syntaxList.AssertListCount(3); 22 | var faceOuterBound = new StepFaceOuterBound(); 23 | faceOuterBound.Name = syntaxList.Values[0].GetStringValue(); 24 | binder.BindValue(syntaxList.Values[1], v => faceOuterBound.Bound = v.AsType()); 25 | faceOuterBound.Orientation = syntaxList.Values[2].GetBooleanValue(); 26 | return faceOuterBound; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepFaceSurface.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public abstract class StepFaceSurface : StepFace 7 | { 8 | public StepSurface FaceGeometry { get; set; } 9 | 10 | public bool SameSense { get; set; } 11 | 12 | public StepFaceSurface(string name) 13 | : base(name) 14 | { 15 | } 16 | 17 | internal override IEnumerable GetParameters(StepWriter writer) 18 | { 19 | foreach (var parameter in base.GetParameters(writer)) 20 | { 21 | yield return parameter; 22 | } 23 | 24 | yield return writer.GetItemSyntax(FaceGeometry); 25 | yield return StepWriter.GetBooleanSyntax(!SameSense); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepGeometricRepresentationItem.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepGeometricRepresentationItem : StepRepresentationItem 4 | { 5 | protected StepGeometricRepresentationItem(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepItemType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IxMilia.Step.Items 4 | { 5 | public enum StepItemType 6 | { 7 | AdvancedFace, 8 | AxisPlacement2D, 9 | AxisPlacement3D, 10 | BSplineCurveWithKnots, 11 | CartesianPoint, 12 | Circle, 13 | CylindricalSurface, 14 | Direction, 15 | EdgeCurve, 16 | EdgeLoop, 17 | Ellipse, 18 | FaceBound, 19 | FaceOuterBound, 20 | Line, 21 | OrientedEdge, 22 | Plane, 23 | Vector, 24 | VertexPoint 25 | } 26 | 27 | internal static class StepItemTypeExtensions 28 | { 29 | public const string AdvancedFaceText = "ADVANCED_FACE"; 30 | public const string Axis2Placement2DText = "AXIS2_PLACEMENT_2D"; 31 | public const string Axis2Placement3DText = "AXIS2_PLACEMENT_3D"; 32 | public const string BSplineCurveWithKnotsText = "B_SPLINE_CURVE_WITH_KNOTS"; 33 | public const string CartesianPointText = "CARTESIAN_POINT"; 34 | public const string CircleText = "CIRCLE"; 35 | public const string CylindricalSurfaceText = "CYLINDRICAL_SURFACE"; 36 | public const string DirectionText = "DIRECTION"; 37 | public const string EdgeCurveText = "EDGE_CURVE"; 38 | public const string EdgeLoopText = "EDGE_LOOP"; 39 | public const string EllipseText = "ELLIPSE"; 40 | public const string FaceBoundText = "FACE_BOUND"; 41 | public const string FaceOuterBoundText = "FACE_OUTER_BOUND"; 42 | public const string LineText = "LINE"; 43 | public const string OrientedEdgeText = "ORIENTED_EDGE"; 44 | public const string PlaneText = "PLANE"; 45 | public const string VectorText = "VECTOR"; 46 | public const string VertexPointText = "VERTEX_POINT"; 47 | 48 | public static string GetItemTypeString(this StepItemType type) 49 | { 50 | switch (type) 51 | { 52 | case StepItemType.AdvancedFace: 53 | return AdvancedFaceText; 54 | case StepItemType.AxisPlacement2D: 55 | return Axis2Placement2DText; 56 | case StepItemType.AxisPlacement3D: 57 | return Axis2Placement3DText; 58 | case StepItemType.BSplineCurveWithKnots: 59 | return BSplineCurveWithKnotsText; 60 | case StepItemType.CartesianPoint: 61 | return CartesianPointText; 62 | case StepItemType.Circle: 63 | return CircleText; 64 | case StepItemType.CylindricalSurface: 65 | return CylindricalSurfaceText; 66 | case StepItemType.Direction: 67 | return DirectionText; 68 | case StepItemType.EdgeCurve: 69 | return EdgeCurveText; 70 | case StepItemType.EdgeLoop: 71 | return EdgeLoopText; 72 | case StepItemType.Ellipse: 73 | return EllipseText; 74 | case StepItemType.FaceBound: 75 | return FaceBoundText; 76 | case StepItemType.FaceOuterBound: 77 | return FaceOuterBoundText; 78 | case StepItemType.Line: 79 | return LineText; 80 | case StepItemType.OrientedEdge: 81 | return OrientedEdgeText; 82 | case StepItemType.Plane: 83 | return PlaneText; 84 | case StepItemType.Vector: 85 | return VectorText; 86 | case StepItemType.VertexPoint: 87 | return VertexPointText; 88 | default: 89 | throw new InvalidOperationException("Unexpected item type " + type); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepLine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepLine : StepCurve 8 | { 9 | public override StepItemType ItemType => StepItemType.Line; 10 | 11 | private StepCartesianPoint _point; 12 | private StepVector _vector; 13 | 14 | public StepCartesianPoint Point 15 | { 16 | get { return _point; } 17 | set 18 | { 19 | if (value == null) 20 | { 21 | throw new ArgumentNullException(); 22 | } 23 | 24 | _point = value; 25 | } 26 | } 27 | 28 | public StepVector Vector 29 | { 30 | get { return _vector; } 31 | set 32 | { 33 | if (value == null) 34 | { 35 | throw new ArgumentNullException(); 36 | } 37 | 38 | _vector = value; 39 | } 40 | } 41 | 42 | private StepLine() 43 | : base(string.Empty) 44 | { 45 | } 46 | 47 | public StepLine(string label, StepCartesianPoint point, StepVector vector) 48 | : base(label) 49 | { 50 | Point = point; 51 | Vector = vector; 52 | } 53 | 54 | public static StepLine FromPoints(double x1, double y1, double z1, double x2, double y2, double z2) 55 | { 56 | var start = new StepCartesianPoint("", x1, y1, z1); 57 | var dx = x2 - x1; 58 | var dy = y2 - y1; 59 | var dz = z2 - z1; 60 | var length = Math.Sqrt(dx * dx + dy * dy + dz * dz); 61 | var dxn = dx / length; 62 | var dyn = dy / length; 63 | var dzn = dz / length; 64 | var vector = new StepVector("", new StepDirection("", dxn, dyn, dzn), length); 65 | return new StepLine("", start, vector); 66 | } 67 | 68 | internal override IEnumerable GetReferencedItems() 69 | { 70 | yield return Point; 71 | yield return Vector; 72 | } 73 | 74 | internal override IEnumerable GetParameters(StepWriter writer) 75 | { 76 | foreach (var parameter in base.GetParameters(writer)) 77 | { 78 | yield return parameter; 79 | } 80 | 81 | yield return writer.GetItemSyntax(Point); 82 | yield return writer.GetItemSyntax(Vector); 83 | } 84 | 85 | internal static StepLine CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 86 | { 87 | var line = new StepLine(); 88 | syntaxList.AssertListCount(3); 89 | line.Name = syntaxList.Values[0].GetStringValue(); 90 | binder.BindValue(syntaxList.Values[1], v => line.Point = v.AsType()); 91 | binder.BindValue(syntaxList.Values[2], v => line.Vector = v.AsType()); 92 | return line; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepLoop.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepLoop : StepTopologicalRepresentationItem 4 | { 5 | public StepLoop(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepOrientedEdge.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepOrientedEdge : StepEdge 8 | { 9 | public override StepItemType ItemType => StepItemType.OrientedEdge; 10 | 11 | private StepEdge _edgeElement; 12 | 13 | public StepEdge EdgeElement 14 | { 15 | get { return _edgeElement; } 16 | set 17 | { 18 | if (value == null) 19 | { 20 | throw new ArgumentNullException(); 21 | } 22 | 23 | _edgeElement = value; 24 | } 25 | } 26 | 27 | public bool Orientation { get; set; } 28 | 29 | private StepOrientedEdge() 30 | { 31 | } 32 | 33 | public StepOrientedEdge(string name, StepVertex edgeStart, StepVertex edgeEnd, StepEdge edgeElement, bool orientation) 34 | : base(name, edgeStart, edgeEnd) 35 | { 36 | EdgeElement = edgeElement; 37 | Orientation = orientation; 38 | } 39 | 40 | internal override IEnumerable GetReferencedItems() 41 | { 42 | foreach (var item in base.GetReferencedItems()) 43 | { 44 | yield return item; 45 | } 46 | 47 | yield return EdgeElement; 48 | } 49 | 50 | internal override IEnumerable GetParameters(StepWriter writer) 51 | { 52 | foreach (var parameter in base.GetParameters(writer)) 53 | { 54 | yield return parameter; 55 | } 56 | 57 | yield return writer.GetItemSyntax(EdgeElement); 58 | yield return StepWriter.GetBooleanSyntax(Orientation); 59 | } 60 | 61 | internal static StepOrientedEdge CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 62 | { 63 | var orientedEdge = new StepOrientedEdge(); 64 | syntaxList.AssertListCount(5); 65 | orientedEdge.Name = syntaxList.Values[0].GetStringValue(); 66 | binder.BindValue(syntaxList.Values[1], v => orientedEdge.EdgeStart = v.AsType()); 67 | binder.BindValue(syntaxList.Values[2], v => orientedEdge.EdgeEnd = v.AsType()); 68 | binder.BindValue(syntaxList.Values[3], v => orientedEdge.EdgeElement = v.AsType()); 69 | orientedEdge.Orientation = syntaxList.Values[4].GetBooleanValue(); 70 | return orientedEdge; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepPlacement.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepPlacement : StepGeometricRepresentationItem 4 | { 5 | protected StepPlacement(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepPlane.cs: -------------------------------------------------------------------------------- 1 | using IxMilia.Step.Syntax; 2 | 3 | namespace IxMilia.Step.Items 4 | { 5 | public class StepPlane : StepElementarySurface 6 | { 7 | public override StepItemType ItemType => StepItemType.Plane; 8 | 9 | private StepPlane() 10 | : base() 11 | { 12 | } 13 | 14 | public StepPlane(string name, StepAxis2Placement3D position) 15 | : base(name, position) 16 | { 17 | } 18 | 19 | internal static StepPlane CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 20 | { 21 | var plane = new StepPlane(); 22 | syntaxList.AssertListCount(2); 23 | plane.Name = syntaxList.Values[0].GetStringValue(); 24 | binder.BindValue(syntaxList.Values[1], v => plane.Position = v.AsType()); 25 | return plane; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepPoint.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepPoint : StepGeometricRepresentationItem 4 | { 5 | protected StepPoint(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepRepresentationItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public abstract partial class StepRepresentationItem 7 | { 8 | public abstract StepItemType ItemType { get; } 9 | 10 | public string Name { get; set; } 11 | 12 | protected StepRepresentationItem(string name) 13 | { 14 | Name = name; 15 | } 16 | 17 | internal virtual IEnumerable GetReferencedItems() 18 | { 19 | yield break; 20 | } 21 | 22 | internal virtual IEnumerable GetParameters(StepWriter writer) 23 | { 24 | yield return new StepStringSyntax(Name); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepRepresentationItem_FromTypedParameter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public abstract partial class StepRepresentationItem 8 | { 9 | internal static HashSet UnsupportedItemTypes { get; } = new HashSet(); 10 | 11 | internal static StepRepresentationItem FromTypedParameter(StepBinder binder, StepItemSyntax itemSyntax) 12 | { 13 | StepRepresentationItem item = null; 14 | if (itemSyntax is StepSimpleItemSyntax) 15 | { 16 | var simpleItem = (StepSimpleItemSyntax)itemSyntax; 17 | switch (simpleItem.Keyword) 18 | { 19 | case StepItemTypeExtensions.AdvancedFaceText: 20 | item = StepAdvancedFace.CreateFromSyntaxList(binder, simpleItem.Parameters); 21 | break; 22 | case StepItemTypeExtensions.Axis2Placement2DText: 23 | item = StepAxis2Placement2D.CreateFromSyntaxList(binder, simpleItem.Parameters); 24 | break; 25 | case StepItemTypeExtensions.Axis2Placement3DText: 26 | item = StepAxis2Placement3D.CreateFromSyntaxList(binder, simpleItem.Parameters); 27 | break; 28 | case StepItemTypeExtensions.BSplineCurveWithKnotsText: 29 | item = StepBSplineCurveWithKnots.CreateFromSyntaxList(binder, simpleItem.Parameters); 30 | break; 31 | case StepItemTypeExtensions.CartesianPointText: 32 | item = StepCartesianPoint.CreateFromSyntaxList(simpleItem.Parameters); 33 | break; 34 | case StepItemTypeExtensions.CircleText: 35 | item = StepCircle.CreateFromSyntaxList(binder, simpleItem.Parameters); 36 | break; 37 | case StepItemTypeExtensions.CylindricalSurfaceText: 38 | item = StepCylindricalSurface.CreateFromSyntaxList(binder, simpleItem.Parameters); 39 | break; 40 | case StepItemTypeExtensions.DirectionText: 41 | item = StepDirection.CreateFromSyntaxList(simpleItem.Parameters); 42 | break; 43 | case StepItemTypeExtensions.EdgeCurveText: 44 | item = StepEdgeCurve.CreateFromSyntaxList(binder, simpleItem.Parameters); 45 | break; 46 | case StepItemTypeExtensions.EdgeLoopText: 47 | item = StepEdgeLoop.CreateFromSyntaxList(binder, simpleItem.Parameters); 48 | break; 49 | case StepItemTypeExtensions.EllipseText: 50 | item = StepEllipse.CreateFromSyntaxList(binder, simpleItem.Parameters); 51 | break; 52 | case StepItemTypeExtensions.FaceBoundText: 53 | item = StepFaceBound.CreateFromSyntaxList(binder, simpleItem.Parameters); 54 | break; 55 | case StepItemTypeExtensions.FaceOuterBoundText: 56 | item = StepFaceOuterBound.CreateFromSyntaxList(binder, simpleItem.Parameters); 57 | break; 58 | case StepItemTypeExtensions.LineText: 59 | item = StepLine.CreateFromSyntaxList(binder, simpleItem.Parameters); 60 | break; 61 | case StepItemTypeExtensions.OrientedEdgeText: 62 | item = StepOrientedEdge.CreateFromSyntaxList(binder, simpleItem.Parameters); 63 | break; 64 | case StepItemTypeExtensions.PlaneText: 65 | item = StepPlane.CreateFromSyntaxList(binder, simpleItem.Parameters); 66 | break; 67 | case StepItemTypeExtensions.VectorText: 68 | item = StepVector.CreateFromSyntaxList(binder, simpleItem.Parameters); 69 | break; 70 | case StepItemTypeExtensions.VertexPointText: 71 | item = StepVertexPoint.CreateFromSyntaxList(binder, simpleItem.Parameters); 72 | break; 73 | default: 74 | if (UnsupportedItemTypes.Add(simpleItem.Keyword)) 75 | { 76 | Debug.WriteLine($"Unsupported item {simpleItem.Keyword} at {simpleItem.Line}, {simpleItem.Column}"); 77 | } 78 | break; 79 | } 80 | } 81 | else 82 | { 83 | // TODO: 84 | } 85 | 86 | return item; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepSurface.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepSurface : StepGeometricRepresentationItem 4 | { 5 | public StepSurface(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepTopologicalRepresentationItem.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepTopologicalRepresentationItem : StepRepresentationItem 4 | { 5 | protected StepTopologicalRepresentationItem(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepTriple.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step.Items 5 | { 6 | public abstract class StepTriple : StepPoint 7 | { 8 | public double X { get; set; } 9 | public double Y { get; set; } 10 | public double Z { get; set; } 11 | 12 | protected abstract int MinimumValueCount { get; } 13 | 14 | protected StepTriple() 15 | : this(string.Empty, 0.0, 0.0, 0.0) 16 | { 17 | } 18 | 19 | protected StepTriple(string name, double x, double y, double z) 20 | : base(name) 21 | { 22 | X = x; 23 | Y = y; 24 | Z = z; 25 | } 26 | 27 | internal override IEnumerable GetParameters(StepWriter writer) 28 | { 29 | foreach (var parameter in base.GetParameters(writer)) 30 | { 31 | yield return parameter; 32 | } 33 | 34 | yield return new StepSyntaxList( 35 | new StepRealSyntax(X), 36 | new StepRealSyntax(Y), 37 | new StepRealSyntax(Z) 38 | ); 39 | } 40 | 41 | internal static StepTriple AssignTo(StepTriple triple, StepSyntaxList values) 42 | { 43 | values.AssertListCount(2); 44 | triple.Name = values.Values[0].GetStringValue(); 45 | var pointValues = values.Values[1].GetValueList(); 46 | pointValues.AssertListCount(triple.MinimumValueCount, 3); 47 | triple.X = pointValues.GetRealValueOrDefault(0); 48 | triple.Y = pointValues.GetRealValueOrDefault(1); 49 | triple.Z = pointValues.GetRealValueOrDefault(2); 50 | return triple; 51 | } 52 | 53 | public bool Equals(StepTriple other) 54 | { 55 | if ((object)other == null) 56 | { 57 | return false; 58 | } 59 | 60 | return ItemType == other.ItemType && X == other.X && Y == other.Y && Z == other.Z && Name == other.Name; 61 | } 62 | 63 | public override bool Equals(object obj) 64 | { 65 | return Equals(obj as StepTriple); 66 | } 67 | 68 | public override int GetHashCode() 69 | { 70 | return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); 71 | } 72 | 73 | public static bool operator ==(StepTriple left, StepTriple right) 74 | { 75 | if (ReferenceEquals(left, right)) 76 | { 77 | return true; 78 | } 79 | 80 | if ((object)left == null || (object)right == null) 81 | { 82 | return false; 83 | } 84 | 85 | return left.Equals(right); 86 | } 87 | 88 | public static bool operator !=(StepTriple left, StepTriple right) 89 | { 90 | return !(left == right); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepVector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepVector : StepGeometricRepresentationItem 8 | { 9 | public override StepItemType ItemType => StepItemType.Vector; 10 | 11 | private StepDirection _direction; 12 | public StepDirection Direction 13 | { 14 | get { return _direction; } 15 | set 16 | { 17 | if (value == null) 18 | { 19 | throw new ArgumentNullException(); 20 | } 21 | 22 | _direction = value; 23 | } 24 | } 25 | public double Length { get; set; } 26 | 27 | private StepVector() 28 | : base(string.Empty) 29 | { 30 | } 31 | 32 | public StepVector(string name, StepDirection direction, double length) 33 | : base(name) 34 | { 35 | Direction = direction; 36 | Length = length; 37 | } 38 | 39 | internal override IEnumerable GetReferencedItems() 40 | { 41 | yield return Direction; 42 | } 43 | 44 | internal override IEnumerable GetParameters(StepWriter writer) 45 | { 46 | foreach (var parameter in base.GetParameters(writer)) 47 | { 48 | yield return parameter; 49 | } 50 | 51 | yield return writer.GetItemSyntax(Direction); 52 | yield return new StepRealSyntax(Length); 53 | } 54 | 55 | internal static StepVector CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 56 | { 57 | var vector = new StepVector(); 58 | syntaxList.AssertListCount(3); 59 | vector.Name = syntaxList.Values[0].GetStringValue(); 60 | binder.BindValue(syntaxList.Values[1], v => vector.Direction = v.AsType()); 61 | vector.Length = syntaxList.Values[2].GetRealVavlue(); 62 | return vector; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepVertex.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Items 2 | { 3 | public abstract class StepVertex : StepTopologicalRepresentationItem 4 | { 5 | protected StepVertex(string name) 6 | : base(name) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Items/StepVertexPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Syntax; 4 | 5 | namespace IxMilia.Step.Items 6 | { 7 | public class StepVertexPoint : StepVertex 8 | { 9 | public override StepItemType ItemType => StepItemType.VertexPoint; 10 | 11 | private StepCartesianPoint _location; 12 | 13 | public StepCartesianPoint Location 14 | { 15 | get { return _location; } 16 | set 17 | { 18 | if (value == null) 19 | { 20 | throw new ArgumentNullException(); 21 | } 22 | 23 | _location = value; 24 | } 25 | } 26 | 27 | private StepVertexPoint() 28 | : base(string.Empty) 29 | { 30 | } 31 | 32 | public StepVertexPoint(string name, StepCartesianPoint location) 33 | : base(name) 34 | { 35 | Location = location; 36 | } 37 | 38 | internal override IEnumerable GetReferencedItems() 39 | { 40 | yield return Location; 41 | } 42 | 43 | internal override IEnumerable GetParameters(StepWriter writer) 44 | { 45 | foreach (var parameter in base.GetParameters(writer)) 46 | { 47 | yield return parameter; 48 | } 49 | 50 | yield return writer.GetItemSyntax(Location); 51 | } 52 | 53 | internal static StepVertexPoint CreateFromSyntaxList(StepBinder binder, StepSyntaxList syntaxList) 54 | { 55 | var vertex = new StepVertexPoint(); 56 | syntaxList.AssertListCount(2); 57 | vertex.Name = syntaxList.Values[0].GetStringValue(); 58 | binder.BindValue(syntaxList.Values[1], v => vertex.Location = v.AsType()); 59 | return vertex; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/IxMilia.Step/IxMilia.Step.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | A portable .NET library for reading and writing STEP CAD files. 5 | Copyright 2017 6 | IxMilia.Step 7 | IxMilia 8 | netstandard2.0 9 | IxMilia.Step 10 | IxMilia.Step 11 | CAD;STEP;STP 12 | https://github.com/ixmilia/step 13 | MIT 14 | 15 | 16 | true 17 | true 18 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("IxMilia.Step.Test")] 4 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IxMilia.Step.Items; 5 | using IxMilia.Step.Syntax; 6 | 7 | namespace IxMilia.Step 8 | { 9 | internal class StepBinder 10 | { 11 | private Dictionary _itemMap; 12 | private Dictionary>>> _unboundPointers = new Dictionary>>>(); 13 | 14 | public StepBinder(Dictionary itemMap) 15 | { 16 | _itemMap = itemMap; 17 | } 18 | 19 | public void BindValue(StepSyntax syntax, Action bindAction) 20 | { 21 | if (syntax is StepSimpleItemSyntax) 22 | { 23 | var typedParameter = (StepSimpleItemSyntax)syntax; 24 | var item = StepRepresentationItem.FromTypedParameter(this, typedParameter); 25 | var boundItem = new StepBoundItem(item, syntax); 26 | bindAction(boundItem); 27 | } 28 | else if (syntax is StepEntityInstanceReferenceSyntax) 29 | { 30 | var itemInstance = (StepEntityInstanceReferenceSyntax)syntax; 31 | if (_itemMap.ContainsKey(itemInstance.Id)) 32 | { 33 | // pointer already defined, bind immediately 34 | var boundItem = new StepBoundItem(_itemMap[itemInstance.Id], syntax); 35 | bindAction(boundItem); 36 | } 37 | else 38 | { 39 | // not already defined, save it for later 40 | if (!_unboundPointers.ContainsKey(itemInstance.Id)) 41 | { 42 | _unboundPointers.Add(itemInstance.Id, new List>>()); 43 | } 44 | 45 | _unboundPointers[itemInstance.Id].Add(Tuple.Create(syntax, bindAction)); 46 | } 47 | } 48 | else if (syntax is StepAutoSyntax) 49 | { 50 | bindAction(StepBoundItem.AutoItem(syntax)); 51 | } 52 | else 53 | { 54 | throw new StepReadException("Unable to bind pointer, this should be unreachable", syntax.Line, syntax.Column); 55 | } 56 | } 57 | 58 | public void BindRemainingValues() 59 | { 60 | foreach (var id in _unboundPointers.Keys) 61 | { 62 | if (!_itemMap.ContainsKey(id)) 63 | { 64 | var syntax = _unboundPointers[id].First().Item1; 65 | throw new StepReadException($"Cannot bind undefined pointer {id}", syntax.Line, syntax.Column); 66 | } 67 | 68 | var item = _itemMap[id]; 69 | foreach (var binder in _unboundPointers[id]) 70 | { 71 | var boundItem = new StepBoundItem(item, binder.Item1); 72 | binder.Item2(boundItem); 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepBoundItem.cs: -------------------------------------------------------------------------------- 1 | using IxMilia.Step.Items; 2 | using IxMilia.Step.Syntax; 3 | 4 | namespace IxMilia.Step 5 | { 6 | internal class StepBoundItem 7 | { 8 | public StepSyntax CreatingSyntax { get; } 9 | public StepRepresentationItem Item { get; } 10 | public bool IsAuto { get; private set; } 11 | 12 | public StepBoundItem(StepRepresentationItem item, StepSyntax creatingSyntax) 13 | { 14 | CreatingSyntax = creatingSyntax; 15 | Item = item; 16 | } 17 | 18 | public TItemType AsType() where TItemType : StepRepresentationItem 19 | { 20 | TItemType result = null; 21 | if (IsAuto) 22 | { 23 | // do nothing; null is expected 24 | } 25 | else 26 | { 27 | result = Item as TItemType; 28 | if (result == null) 29 | { 30 | throw new StepReadException("Unexpected type", CreatingSyntax.Line, CreatingSyntax.Column); 31 | } 32 | } 33 | 34 | return result; 35 | } 36 | 37 | public static StepBoundItem AutoItem(StepSyntax creatingSyntax) 38 | { 39 | var boundItem = new StepBoundItem(null, creatingSyntax); 40 | boundItem.IsAuto = true; 41 | return boundItem; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using IxMilia.Step.Items; 6 | using IxMilia.Step.Syntax; 7 | 8 | namespace IxMilia.Step 9 | { 10 | public class StepFile 11 | { 12 | internal const string MagicHeader = "ISO-10303-21"; 13 | internal const string MagicFooter = "END-" + MagicHeader; 14 | internal const string HeaderText = "HEADER"; 15 | internal const string EndSectionText = "ENDSEC"; 16 | internal const string DataText = "DATA"; 17 | 18 | internal const string FileDescriptionText = "FILE_DESCRIPTION"; 19 | internal const string FileNameText = "FILE_NAME"; 20 | internal const string FileSchemaText = "FILE_SCHEMA"; 21 | 22 | // FILE_DESCRIPTION values 23 | public string Description { get; set; } 24 | public string ImplementationLevel { get; set; } 25 | 26 | // FILE_NAME values 27 | public string Name { get; set; } 28 | public DateTime Timestamp { get; set; } 29 | public string Author { get; set; } 30 | public string Organization { get; set; } 31 | public string PreprocessorVersion { get; set; } 32 | public string OriginatingSystem { get; set; } 33 | public string Authorization { get; set; } 34 | 35 | // FILE_SCHEMA values 36 | public HashSet Schemas { get; } 37 | public List UnsupportedSchemas { get; } 38 | 39 | public List Items { get; } 40 | 41 | public StepFile() 42 | { 43 | Timestamp = DateTime.Now; 44 | Schemas = new HashSet(); 45 | UnsupportedSchemas = new List(); 46 | Items = new List(); 47 | } 48 | 49 | public static StepFile Load(string path) 50 | { 51 | using (var stream = new FileStream(path, FileMode.Open)) 52 | { 53 | return Load(stream); 54 | } 55 | } 56 | 57 | public static StepFile Load(Stream stream) 58 | { 59 | return new StepReader(stream).ReadFile(); 60 | } 61 | 62 | public static StepFile Parse(string data) 63 | { 64 | using (var stream = new MemoryStream()) 65 | using (var writer = new StreamWriter(stream)) 66 | { 67 | writer.Write(data); 68 | writer.Flush(); 69 | stream.Seek(0, SeekOrigin.Begin); 70 | return Load(stream); 71 | } 72 | } 73 | 74 | public string GetContentsAsString(bool inlineReferences = false) 75 | { 76 | var writer = new StepWriter(this, inlineReferences); 77 | return writer.GetContents(); 78 | } 79 | 80 | public void Save(string path, bool inlineReferences = false) 81 | { 82 | using (var stream = new FileStream(path, FileMode.Create)) 83 | { 84 | Save(stream, inlineReferences); 85 | } 86 | } 87 | 88 | public void Save(Stream stream, bool inlineReferences = false) 89 | { 90 | using (var streamWriter = new StreamWriter(stream)) 91 | { 92 | streamWriter.Write(GetContentsAsString(inlineReferences)); 93 | streamWriter.Flush(); 94 | } 95 | } 96 | 97 | /// 98 | /// Gets all top-level items (i.e., not referenced by any other item) in the file. 99 | /// 100 | public IEnumerable GetTopLevelItems() 101 | { 102 | var visitedItems = new HashSet(); 103 | var referencedItems = new HashSet(); 104 | foreach (var item in Items) 105 | { 106 | MarkReferencedItems(item, visitedItems, referencedItems); 107 | } 108 | 109 | return Items.Where(item => !referencedItems.Contains(item)); 110 | } 111 | 112 | private static void MarkReferencedItems(StepRepresentationItem item, HashSet visitedItems, HashSet referencedItems) 113 | { 114 | if (visitedItems.Add(item)) 115 | { 116 | foreach (var referenced in item.GetReferencedItems()) 117 | { 118 | referencedItems.Add(referenced); 119 | MarkReferencedItems(referenced, visitedItems, referencedItems); 120 | } 121 | } 122 | } 123 | 124 | internal StepHeaderSectionSyntax GetHeaderSyntax() 125 | { 126 | var macros = new List() 127 | { 128 | new StepHeaderMacroSyntax( 129 | FileDescriptionText, 130 | new StepSyntaxList( 131 | new StepSyntaxList(StepWriter.SplitStringIntoParts(Description).Select(s => new StepStringSyntax(s))), 132 | new StepStringSyntax(ImplementationLevel))), 133 | new StepHeaderMacroSyntax( 134 | FileNameText, 135 | new StepSyntaxList( 136 | new StepStringSyntax(Name), 137 | new StepStringSyntax(Timestamp.ToString("O")), 138 | new StepSyntaxList(StepWriter.SplitStringIntoParts(Author).Select(s => new StepStringSyntax(s))), 139 | new StepSyntaxList(StepWriter.SplitStringIntoParts(Organization).Select(s => new StepStringSyntax(s))), 140 | new StepStringSyntax(PreprocessorVersion), 141 | new StepStringSyntax(OriginatingSystem), 142 | new StepStringSyntax(Authorization))), 143 | new StepHeaderMacroSyntax( 144 | FileSchemaText, 145 | new StepSyntaxList( 146 | new StepSyntaxList( 147 | Schemas 148 | .Select(s => s.ToSchemaName()) 149 | .Concat(UnsupportedSchemas) 150 | .Select(s => new StepStringSyntax(s))))) 151 | }; 152 | 153 | return new StepHeaderSectionSyntax(-1, -1, macros); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepLexer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Syntax; 3 | using IxMilia.Step.Tokens; 4 | 5 | namespace IxMilia.Step 6 | { 7 | internal class StepLexer 8 | { 9 | private List _tokens; 10 | private int _offset = 0; 11 | 12 | public StepLexer(IEnumerable tokens) 13 | { 14 | _tokens = new List(tokens); 15 | } 16 | 17 | private bool TokensRemain() 18 | { 19 | return _offset < _tokens.Count; 20 | } 21 | 22 | private void MoveNext() 23 | { 24 | _offset++; 25 | } 26 | 27 | private StepToken Current => _tokens[_offset]; 28 | 29 | public StepFileSyntax LexFileSyntax() 30 | { 31 | _offset = 0; 32 | SwallowKeywordAndSemicolon(StepFile.MagicHeader); 33 | 34 | var header = LexHeaderSection(); 35 | var data = LexDataSection(); 36 | 37 | var file = new StepFileSyntax(header, data); 38 | 39 | SwallowKeywordAndSemicolon(StepFile.MagicFooter); 40 | 41 | return file; 42 | } 43 | 44 | private StepHeaderSectionSyntax LexHeaderSection() 45 | { 46 | AssertTokensRemain(); 47 | var headerLine = Current.Line; 48 | var headerColumn = Current.Column; 49 | SwallowKeywordAndSemicolon(StepFile.HeaderText); 50 | var macros = new List(); 51 | while (TokensRemain() && Current.Kind == StepTokenKind.Keyword && !IsCurrentEndSec()) 52 | { 53 | var macro = LexHeaderMacro(); 54 | macros.Add(macro); 55 | } 56 | 57 | SwallowKeywordAndSemicolon(StepFile.EndSectionText); 58 | return new StepHeaderSectionSyntax(headerLine, headerColumn, macros); 59 | } 60 | 61 | private StepHeaderMacroSyntax LexHeaderMacro() 62 | { 63 | AssertNextTokenKind(StepTokenKind.Keyword); 64 | var name = ((StepKeywordToken)Current).Value; 65 | MoveNext(); 66 | var syntaxList = LexSyntaxList(); 67 | SwallowSemicolon(); 68 | return new StepHeaderMacroSyntax(name, syntaxList); 69 | } 70 | 71 | private StepSyntax LexIndividualValue() 72 | { 73 | StepSyntax result; 74 | AssertTokensRemain(); 75 | switch (Current.Kind) 76 | { 77 | case StepTokenKind.Integer: 78 | result = new StepIntegerSyntax((StepIntegerToken)Current); 79 | MoveNext(); 80 | break; 81 | case StepTokenKind.Real: 82 | result = new StepRealSyntax((StepRealToken)Current); 83 | MoveNext(); 84 | break; 85 | case StepTokenKind.String: 86 | result = new StepStringSyntax((StepStringToken)Current); 87 | MoveNext(); 88 | break; 89 | case StepTokenKind.Asterisk: 90 | result = new StepAutoSyntax((StepAsteriskToken)Current); 91 | MoveNext(); 92 | break; 93 | case StepTokenKind.Omitted: 94 | result = new StepOmittedSyntax((StepOmittedToken)Current); 95 | MoveNext(); 96 | break; 97 | case StepTokenKind.Enumeration: 98 | result = new StepEnumerationValueSyntax((StepEnumerationToken)Current); 99 | MoveNext(); 100 | break; 101 | case StepTokenKind.LeftParen: 102 | result = LexSyntaxList(); 103 | break; 104 | case StepTokenKind.Keyword: 105 | result = LexSimpleItem(); 106 | break; 107 | case StepTokenKind.EntityInstance: 108 | result = new StepEntityInstanceReferenceSyntax((StepEntityInstanceToken)Current); 109 | MoveNext(); 110 | break; 111 | default: 112 | ReportError($"Unexpected syntax token '{Current.Kind}'"); 113 | result = null; // unreachable 114 | break; 115 | } 116 | 117 | return result; 118 | } 119 | 120 | private StepSyntaxList LexSyntaxList() 121 | { 122 | AssertTokensRemain(); 123 | var listLine = Current.Line; 124 | var listColumn = Current.Column; 125 | SwallowLeftParen(); 126 | var values = new List(); 127 | bool keepReading = true; 128 | bool expectingValue = true; 129 | while (keepReading) 130 | { 131 | AssertTokensRemain(); 132 | if (expectingValue || values.Count == 0) 133 | { 134 | // expect a value or a close paren 135 | switch (Current.Kind) 136 | { 137 | case StepTokenKind.RightParen: 138 | keepReading = false; 139 | MoveNext(); 140 | break; 141 | default: 142 | values.Add(LexIndividualValue()); 143 | break; 144 | } 145 | } 146 | else 147 | { 148 | // expect a comma or close paren 149 | switch (Current.Kind) 150 | { 151 | case StepTokenKind.RightParen: 152 | keepReading = false; 153 | MoveNext(); 154 | break; 155 | case StepTokenKind.Comma: 156 | MoveNext(); 157 | break; 158 | default: 159 | ReportError($"Expected right paren or comma but found '{Current.Kind}'"); 160 | break; 161 | } 162 | } 163 | 164 | expectingValue = !expectingValue; 165 | } 166 | 167 | return new StepSyntaxList(listLine, listColumn, values); 168 | } 169 | 170 | private StepDataSectionSyntax LexDataSection() 171 | { 172 | AssertTokensRemain(); 173 | var dataLine = Current.Line; 174 | var dataColumn = Current.Column; 175 | SwallowKeywordAndSemicolon(StepFile.DataText); 176 | var itemInstsances = new List(); 177 | while (TokensRemain() && Current.Kind == StepTokenKind.EntityInstance) 178 | { 179 | var itemInstance = LexItemInstance(); 180 | itemInstsances.Add(itemInstance); 181 | } 182 | 183 | SwallowKeywordAndSemicolon(StepFile.EndSectionText); 184 | return new StepDataSectionSyntax(dataLine, dataColumn, itemInstsances); 185 | } 186 | 187 | private StepEntityInstanceSyntax LexItemInstance() 188 | { 189 | var line = Current.Line; 190 | var column = Current.Column; 191 | 192 | AssertNextTokenKind(StepTokenKind.EntityInstance); 193 | var reference = (StepEntityInstanceToken)Current; 194 | MoveNext(); 195 | 196 | SwallowEquals(); 197 | 198 | AssertTokensRemain(); 199 | StepItemSyntax item = null; 200 | switch (Current.Kind) 201 | { 202 | case StepTokenKind.Keyword: 203 | item = LexSimpleItem(); 204 | break; 205 | case StepTokenKind.LeftParen: 206 | item = LexComplexItem(); 207 | break; 208 | default: 209 | ReportError($"Expected left paren but found {Current.Kind}"); 210 | break; // unreachable 211 | } 212 | 213 | SwallowSemicolon(); 214 | 215 | return new StepEntityInstanceSyntax(reference, item); 216 | } 217 | 218 | private StepSimpleItemSyntax LexSimpleItem() 219 | { 220 | AssertNextTokenKind(StepTokenKind.Keyword); 221 | var keyword = (StepKeywordToken)Current; 222 | MoveNext(); 223 | 224 | var parameters = LexSyntaxList(); 225 | return new StepSimpleItemSyntax(keyword, parameters); 226 | } 227 | 228 | private StepComplexItemSyntax LexComplexItem() 229 | { 230 | var entities = new List(); 231 | var itemLine = Current.Line; 232 | var itemColumn = Current.Column; 233 | SwallowLeftParen(); 234 | entities.Add(LexSimpleItem()); // there's always at least one 235 | 236 | bool keepReading = true; 237 | while (keepReading) 238 | { 239 | AssertTokensRemain(); 240 | switch (Current.Kind) 241 | { 242 | case StepTokenKind.RightParen: 243 | SwallowRightParen(); 244 | keepReading = false; 245 | break; 246 | case StepTokenKind.Keyword: 247 | entities.Add(LexSimpleItem()); 248 | break; 249 | default: 250 | ReportError($"Expected right paren or keyword but found {Current.Kind}"); 251 | break; // unreachable 252 | } 253 | } 254 | 255 | return new StepComplexItemSyntax(itemLine, itemColumn, entities); 256 | } 257 | 258 | private bool IsCurrentEndSec() 259 | { 260 | return Current.Kind == StepTokenKind.Keyword && ((StepKeywordToken)Current).Value == StepFile.EndSectionText; 261 | } 262 | 263 | private void SwallowKeyword(string keyword) 264 | { 265 | AssertNextTokenKind(StepTokenKind.Keyword); 266 | if (((StepKeywordToken)Current).Value != keyword) 267 | { 268 | ReportError($"Expected keyword '{keyword}' but found '{((StepKeywordToken)Current).Value}'"); 269 | } 270 | 271 | MoveNext(); 272 | } 273 | 274 | private void SwallowKeywordAndSemicolon(string keyword) 275 | { 276 | SwallowKeyword(keyword); 277 | SwallowSemicolon(); 278 | } 279 | 280 | private void SwallowSemicolon() 281 | { 282 | SwallowToken(StepTokenKind.Semicolon); 283 | } 284 | 285 | private void SwallowLeftParen() 286 | { 287 | SwallowToken(StepTokenKind.LeftParen); 288 | } 289 | 290 | private void SwallowRightParen() 291 | { 292 | SwallowToken(StepTokenKind.RightParen); 293 | } 294 | 295 | private void SwallowEquals() 296 | { 297 | SwallowToken(StepTokenKind.Equals); 298 | } 299 | 300 | private void SwallowToken(StepTokenKind kind) 301 | { 302 | AssertNextTokenKind(kind); 303 | MoveNext(); 304 | } 305 | 306 | private void AssertNextTokenKind(StepTokenKind kind) 307 | { 308 | AssertTokensRemain(); 309 | if (Current.Kind != kind) 310 | { 311 | ReportError($"Expected '{kind}' token but found '{Current.Kind}'"); 312 | } 313 | } 314 | 315 | private void AssertTokensRemain() 316 | { 317 | if (!TokensRemain()) 318 | { 319 | ReportError("Unexpected end of token stream", 0, 0); 320 | } 321 | } 322 | 323 | private void ReportError(string message, int? line = null, int? column = null) 324 | { 325 | throw new StepReadException(message, line ?? Current.Line, column ?? Current.Column); 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepReadException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IxMilia.Step 4 | { 5 | public class StepReadException : Exception 6 | { 7 | public int Line { get; } 8 | public int Column { get; } 9 | 10 | public StepReadException(string message, int line, int column) 11 | : base($"{message} at [{line}:{column}]") 12 | { 13 | Line = line; 14 | Column = column; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using IxMilia.Step.Items; 6 | using IxMilia.Step.Syntax; 7 | 8 | namespace IxMilia.Step 9 | { 10 | internal class StepReader 11 | { 12 | private StepLexer _lexer; 13 | private StepFile _file; 14 | 15 | public StepReader(Stream stream) 16 | { 17 | _file = new StepFile(); 18 | var tokenizer = new StepTokenizer(stream); 19 | _lexer = new StepLexer(tokenizer.GetTokens()); 20 | } 21 | 22 | public StepFile ReadFile() 23 | { 24 | var fileSyntax = _lexer.LexFileSyntax(); 25 | foreach (var headerMacro in fileSyntax.Header.Macros) 26 | { 27 | ApplyHeaderMacro(headerMacro); 28 | } 29 | 30 | var itemMap = new Dictionary(); 31 | var binder = new StepBinder(itemMap); 32 | StepRepresentationItem.UnsupportedItemTypes.Clear(); 33 | foreach (var itemInstance in fileSyntax.Data.ItemInstances) 34 | { 35 | if (itemMap.ContainsKey(itemInstance.Id)) 36 | { 37 | throw new StepReadException("Duplicate item instance", itemInstance.Line, itemInstance.Column); 38 | } 39 | 40 | var item = StepRepresentationItem.FromTypedParameter(binder, itemInstance.SimpleItemInstance); 41 | if (item != null) 42 | { 43 | itemMap.Add(itemInstance.Id, item); 44 | _file.Items.Add(item); 45 | } 46 | } 47 | 48 | binder.BindRemainingValues(); 49 | 50 | return _file; 51 | } 52 | 53 | private void ApplyHeaderMacro(StepHeaderMacroSyntax macro) 54 | { 55 | switch (macro.Name) 56 | { 57 | case StepFile.FileDescriptionText: 58 | ApplyFileDescription(macro.Values); 59 | break; 60 | case StepFile.FileNameText: 61 | ApplyFileName(macro.Values); 62 | break; 63 | case StepFile.FileSchemaText: 64 | ApplyFileSchema(macro.Values); 65 | break; 66 | default: 67 | Debug.WriteLine($"Unsupported header macro '{macro.Name}' at {macro.Line}, {macro.Column}"); 68 | break; 69 | } 70 | } 71 | 72 | private void ApplyFileDescription(StepSyntaxList valueList) 73 | { 74 | valueList.AssertListCount(2); 75 | _file.Description = valueList.Values[0].GetConcatenatedStringValue(); 76 | _file.ImplementationLevel = valueList.Values[1].GetStringValue(); // TODO: handle appropriate values 77 | } 78 | 79 | private void ApplyFileName(StepSyntaxList valueList) 80 | { 81 | valueList.AssertListCount(7); 82 | _file.Name = valueList.Values[0].GetStringValue(); 83 | _file.Timestamp = valueList.Values[1].GetDateTimeValue(); 84 | _file.Author = valueList.Values[2].GetConcatenatedStringValue(); 85 | _file.Organization = valueList.Values[3].GetConcatenatedStringValue(); 86 | _file.PreprocessorVersion = valueList.Values[4].GetStringValue(); 87 | _file.OriginatingSystem = valueList.Values[5].GetStringValue(); 88 | _file.Authorization = valueList.Values[6].GetStringValue(); 89 | } 90 | 91 | private void ApplyFileSchema(StepSyntaxList valueList) 92 | { 93 | valueList.AssertListCount(1); 94 | foreach (var schemaName in valueList.Values[0].GetValueList().Values.Select(v => v.GetStringValue())) 95 | { 96 | StepSchemaTypes schemaType; 97 | if (StepSchemaTypeExtensions.TryGetSchemaTypeFromName(schemaName, out schemaType)) 98 | { 99 | _file.Schemas.Add(schemaType); 100 | } 101 | else 102 | { 103 | _file.UnsupportedSchemas.Add(schemaName); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepSchemaTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IxMilia.Step 4 | { 5 | public enum StepSchemaTypes 6 | { 7 | ExplicitDraughting = 201, 8 | AssociativeDraghting = 202, 9 | ConfigControlDesign = 203, 10 | StructuralAnalysisDesign = 209, 11 | ElectronicAssemblyInterconnect = 210, 12 | AutomotiveDesign = 214, 13 | ShipArrangement = 215, 14 | ShipMouldedForm = 216, 15 | ShipStructures = 218, 16 | DimensionalInspectionSchema = 219, 17 | FunctionalDataAndSchematics = 221, 18 | CastParts = 223, 19 | FeatureBasedProcessPlanning = 224, 20 | BuildingDeisgn = 225, 21 | PlantSpatialConfiguration = 227, 22 | TechnicalDataPackaging = 232, 23 | EngineeringProperties = 235, 24 | FurnitureCatalogAndInteriorDesign = 236, 25 | IntegratedCNC = 238, 26 | ProductLifeCycleSupport = 239, 27 | ProcessPlanning = 240, 28 | ManagedModelBased3DEngineering = 242 29 | } 30 | 31 | internal static class StepSchemaTypeExtensions 32 | { 33 | // SHAPE_APPEARANCE_LAYERS_GROUPS 34 | public const string AssociativeDraghtingText = "ASSOCIATIVE_DRAUGHTING"; 35 | public const string AutomotiveDesignText = "AUTOMOTIVE_DESIGN"; 36 | public const string BuildingDesignText = "BUILDING_DESIGN_SCHEMA"; 37 | public const string CastPartsText = "CAST_PARTS_SCHEMA"; 38 | public const string ConfigControlDesignText = "CONFIG_CONTROL_DESIGN"; 39 | public const string ConfigurationControlled3DDesignText = "AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF"; 40 | public const string DimensionalInspectionText = "DIMENSIONAL_INSPECTION_SCHEMA"; 41 | public const string ElectronicAssemblyInterconnectText = "AP210_ELECTRONIC_ASSEMBLY_INTERCONNECT_AND_PACKAGING_DESIGN_MIM_LF"; 42 | public const string EngineeringPropertiesText = "ENGINEERING_PROPERTIES_SCHEMA"; 43 | public const string ExplicitDraughtingText = "EXPLICIT_DRAUGHTING"; 44 | public const string FeatureBasedProcessPlanningText = "FEATURE_BASED_PROCESS_PLANNING"; 45 | public const string FunctionalDataAndSchematicsText = "FUNCTIONAL_DATA_AND_SCHEMATIC_REPRESENTATION_MIM_LF"; 46 | public const string FurnitureCatalogAndInteriorDesignText = "AP236_FURNITURE_CATALOG_AND_INTERIOR_DESIGN_MIM_LF"; 47 | public const string IntegratedCNCText = "INTEGRATED_CNC_SCHEMA"; 48 | public const string ManagedModelBased3DEngineeringText = "AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF"; 49 | public const string PlantSpatialConfigurationText = "PLANT_SPATIAL_CONFIGURATION"; 50 | public const string ProcessPlanningText = "PROCESS_PLANNING_SCHEMA"; 51 | public const string ProductLifeCycleSupportText = "AP239_PRODUCT_LIFE_CYCLE_SUPPORT_MIM_LF"; 52 | public const string ShipArrangementText = "SHIP_ARRANGEMENT_SCHEMA"; 53 | public const string ShipMouldedFormText = "SHIP_MOULDED_FORM_SCHEMA"; 54 | public const string ShipStructuresText = "SHIP_STRUCTURES_SCHEMA"; 55 | public const string StructuralAnalysisDesignText = "STRUCTURAL_ANALYSIS_DESIGN"; 56 | public const string TechnicalDataPackagingText = "TECHNICAL_DATA_PACKAGING"; 57 | 58 | public static string ToSchemaName(this StepSchemaTypes type) 59 | { 60 | switch (type) 61 | { 62 | case StepSchemaTypes.AssociativeDraghting: 63 | return AssociativeDraghtingText; 64 | case StepSchemaTypes.AutomotiveDesign: 65 | return AutomotiveDesignText; 66 | case StepSchemaTypes.BuildingDeisgn: 67 | return BuildingDesignText; 68 | case StepSchemaTypes.CastParts: 69 | return CastPartsText; 70 | case StepSchemaTypes.ConfigControlDesign: 71 | return ConfigControlDesignText; 72 | case StepSchemaTypes.DimensionalInspectionSchema: 73 | return DimensionalInspectionText; 74 | case StepSchemaTypes.ElectronicAssemblyInterconnect: 75 | return ElectronicAssemblyInterconnectText; 76 | case StepSchemaTypes.EngineeringProperties: 77 | return EngineeringPropertiesText; 78 | case StepSchemaTypes.ExplicitDraughting: 79 | return ExplicitDraughtingText; 80 | case StepSchemaTypes.FeatureBasedProcessPlanning: 81 | return FeatureBasedProcessPlanningText; 82 | case StepSchemaTypes.FunctionalDataAndSchematics: 83 | return FunctionalDataAndSchematicsText; 84 | case StepSchemaTypes.FurnitureCatalogAndInteriorDesign: 85 | return FurnitureCatalogAndInteriorDesignText; 86 | case StepSchemaTypes.IntegratedCNC: 87 | return IntegratedCNCText; 88 | case StepSchemaTypes.ManagedModelBased3DEngineering: 89 | return ManagedModelBased3DEngineeringText; 90 | case StepSchemaTypes.PlantSpatialConfiguration: 91 | return PlantSpatialConfigurationText; 92 | case StepSchemaTypes.ProcessPlanning: 93 | return ProcessPlanningText; 94 | case StepSchemaTypes.ProductLifeCycleSupport: 95 | return ProductLifeCycleSupportText; 96 | case StepSchemaTypes.ShipArrangement: 97 | return ShipArrangementText; 98 | case StepSchemaTypes.ShipMouldedForm: 99 | return ShipMouldedFormText; 100 | case StepSchemaTypes.ShipStructures: 101 | return ShipStructuresText; 102 | case StepSchemaTypes.StructuralAnalysisDesign: 103 | return StructuralAnalysisDesignText; 104 | case StepSchemaTypes.TechnicalDataPackaging: 105 | return TechnicalDataPackagingText; 106 | default: 107 | throw new ArgumentException($"Unsupported schema type '{type}'", nameof(type)); 108 | } 109 | } 110 | 111 | public static bool TryGetSchemaTypeFromName(string schemaName, out StepSchemaTypes schemaType) 112 | { 113 | switch (schemaName) 114 | { 115 | case AssociativeDraghtingText: 116 | schemaType = StepSchemaTypes.AssociativeDraghting; 117 | break; 118 | case AutomotiveDesignText: 119 | schemaType = StepSchemaTypes.AutomotiveDesign; 120 | break; 121 | case BuildingDesignText: 122 | schemaType = StepSchemaTypes.BuildingDeisgn; 123 | break; 124 | case CastPartsText: 125 | schemaType = StepSchemaTypes.CastParts; 126 | break; 127 | case ConfigControlDesignText: 128 | case ConfigurationControlled3DDesignText: 129 | schemaType = StepSchemaTypes.ConfigControlDesign; 130 | break; 131 | case DimensionalInspectionText: 132 | schemaType = StepSchemaTypes.DimensionalInspectionSchema; 133 | break; 134 | case ElectronicAssemblyInterconnectText: 135 | schemaType = StepSchemaTypes.ElectronicAssemblyInterconnect; 136 | break; 137 | case EngineeringPropertiesText: 138 | schemaType = StepSchemaTypes.EngineeringProperties; 139 | break; 140 | case ExplicitDraughtingText: 141 | schemaType = StepSchemaTypes.ExplicitDraughting; 142 | break; 143 | case FeatureBasedProcessPlanningText: 144 | schemaType = StepSchemaTypes.FeatureBasedProcessPlanning; 145 | break; 146 | case FunctionalDataAndSchematicsText: 147 | schemaType = StepSchemaTypes.FunctionalDataAndSchematics; 148 | break; 149 | case FurnitureCatalogAndInteriorDesignText: 150 | schemaType = StepSchemaTypes.FurnitureCatalogAndInteriorDesign; 151 | break; 152 | case IntegratedCNCText: 153 | schemaType = StepSchemaTypes.IntegratedCNC; 154 | break; 155 | case ManagedModelBased3DEngineeringText: 156 | schemaType = StepSchemaTypes.ManagedModelBased3DEngineering; 157 | break; 158 | case PlantSpatialConfigurationText: 159 | schemaType = StepSchemaTypes.PlantSpatialConfiguration; 160 | break; 161 | case ProcessPlanningText: 162 | schemaType = StepSchemaTypes.ProcessPlanning; 163 | break; 164 | case ProductLifeCycleSupportText: 165 | schemaType = StepSchemaTypes.ProductLifeCycleSupport; 166 | break; 167 | case ShipArrangementText: 168 | schemaType = StepSchemaTypes.ShipArrangement; 169 | break; 170 | case ShipMouldedFormText: 171 | schemaType = StepSchemaTypes.ShipMouldedForm; 172 | break; 173 | case ShipStructuresText: 174 | schemaType = StepSchemaTypes.ShipStructures; 175 | break; 176 | case StructuralAnalysisDesignText: 177 | schemaType = StepSchemaTypes.StructuralAnalysisDesign; 178 | break; 179 | case TechnicalDataPackagingText: 180 | schemaType = StepSchemaTypes.TechnicalDataPackaging; 181 | break; 182 | default: 183 | schemaType = default(StepSchemaTypes); 184 | return false; 185 | } 186 | 187 | return true; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepTokenizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Text; 6 | using IxMilia.Step.Tokens; 7 | 8 | namespace IxMilia.Step 9 | { 10 | internal class StepTokenizer 11 | { 12 | private StreamReader _reader; 13 | private string _currentLine; 14 | private int _offset; 15 | private int _currentLineNumber; 16 | private int _currentColumn; 17 | 18 | public int CurrentLine => _currentLineNumber; 19 | public int CurrentColumn => _currentColumn; 20 | 21 | public StepTokenizer(Stream stream) 22 | { 23 | _reader = new StreamReader(stream); 24 | ReadNextLine(); 25 | } 26 | 27 | private void ReadNextLine() 28 | { 29 | _currentLine = _reader.ReadLine(); 30 | _offset = 0; 31 | _currentLineNumber++; 32 | _currentColumn = 1; 33 | } 34 | 35 | private char? PeekCharacter() 36 | { 37 | while (true) 38 | { 39 | if (_currentLine == null) 40 | { 41 | return null; 42 | } 43 | 44 | while (_offset >= _currentLine.Length) 45 | { 46 | ReadNextLine(); 47 | if (_currentLine == null) 48 | { 49 | return null; 50 | } 51 | } 52 | 53 | switch (_currentLine[_offset]) 54 | { 55 | case '/': 56 | if (_offset <= _currentLine.Length - 1 && _currentLine[_offset + 1] == '*') 57 | { 58 | // entered multiline comment 59 | Advance(); // swallow '/' 60 | Advance(); // swallow '*' 61 | 62 | var endIndex = _currentLine.IndexOf("*/", _offset); 63 | while (endIndex < 0 && _currentLine != null) 64 | { 65 | // end wasn't on this line 66 | ReadNextLine(); 67 | if (_currentLine == null) 68 | { 69 | break; 70 | } 71 | 72 | endIndex = _currentLine.IndexOf("*/", _offset); 73 | } 74 | 75 | if (_currentLine == null) 76 | { 77 | // read past the end of the file 78 | return null; 79 | } 80 | else 81 | { 82 | // end was on this line 83 | _offset = endIndex + 2; 84 | } 85 | } 86 | else 87 | { 88 | goto default; 89 | } 90 | break; 91 | default: 92 | return _currentLine[_offset]; 93 | } 94 | } 95 | } 96 | 97 | private void Advance() 98 | { 99 | _offset++; 100 | _currentColumn++; 101 | if (_offset > _currentLine.Length) 102 | { 103 | ReadNextLine(); 104 | } 105 | } 106 | 107 | public IEnumerable GetTokens() 108 | { 109 | char? cn; 110 | SwallowWhitespace(); 111 | while ((cn = PeekCharacter()) != null) 112 | { 113 | var tokenLine = _currentLineNumber; 114 | var tokenColumn = _currentColumn; 115 | var c = cn.GetValueOrDefault(); 116 | if (c == '$') 117 | { 118 | Advance(); 119 | yield return new StepOmittedToken(tokenLine, tokenColumn); 120 | } 121 | else if (c == ';') 122 | { 123 | Advance(); 124 | yield return new StepSemicolonToken(tokenLine, tokenColumn); 125 | } 126 | else if (c == '=') 127 | { 128 | Advance(); 129 | yield return new StepEqualsToken(tokenLine, tokenColumn); 130 | } 131 | else if (c == '*') 132 | { 133 | Advance(); 134 | yield return new StepAsteriskToken(tokenLine, tokenColumn); 135 | } 136 | else if (IsNumberStart(c)) 137 | { 138 | yield return ParseNumber(); 139 | } 140 | else if (IsApostrophe(c)) 141 | { 142 | yield return ParseString(); 143 | } 144 | else if (IsHash(c)) 145 | { 146 | // constant instance: #INCH 147 | // entity instance: #1234 148 | yield return ParseHashValue(); 149 | } 150 | else if (IsAt(c)) 151 | { 152 | // constant value: @PI 153 | // instance value: @12 154 | yield return ParseAtValue(); 155 | } 156 | else if (IsDot(c)) 157 | { 158 | yield return ParseEnumeration(); 159 | } 160 | else if (IsLeftParen(c)) 161 | { 162 | Advance(); 163 | yield return new StepLeftParenToken(tokenLine, tokenColumn); 164 | } 165 | else if (IsRightParen(c)) 166 | { 167 | Advance(); 168 | yield return new StepRightParenToken(tokenLine, tokenColumn); 169 | } 170 | else if (IsComma(c)) 171 | { 172 | Advance(); 173 | yield return new StepCommaToken(tokenLine, tokenColumn); 174 | } 175 | else if (IsUpper(c)) 176 | { 177 | yield return ParseKeyword(); 178 | } 179 | else 180 | { 181 | throw new StepReadException($"Unexpected character '{c}'", _currentLineNumber, _currentColumn); 182 | } 183 | 184 | SwallowWhitespace(); 185 | } 186 | 187 | yield break; 188 | } 189 | 190 | private void SwallowWhitespace() 191 | { 192 | char? cn; 193 | bool keepSwallowing = true; 194 | while (keepSwallowing && (cn = PeekCharacter()) != null) 195 | { 196 | switch (cn.GetValueOrDefault()) 197 | { 198 | case ' ': 199 | case '\r': 200 | case '\n': 201 | case '\t': 202 | case '\f': 203 | case '\v': 204 | Advance(); 205 | break; 206 | default: 207 | keepSwallowing = false; 208 | break; 209 | } 210 | } 211 | } 212 | 213 | private bool IsDigit(char c) 214 | { 215 | return c >= '0' && c <= '9'; 216 | } 217 | 218 | private bool IsDot(char c) 219 | { 220 | return c == '.'; 221 | } 222 | 223 | private bool IsE(char c) 224 | { 225 | return c == 'e' || c == 'E'; 226 | } 227 | 228 | private bool IsPlus(char c) 229 | { 230 | return c == '+'; 231 | } 232 | 233 | private bool IsMinus(char c) 234 | { 235 | return c == '-'; 236 | } 237 | 238 | private bool IsUnderscore(char c) 239 | { 240 | return c == '_'; 241 | } 242 | 243 | private bool IsNumberStart(char c) 244 | { 245 | return IsDigit(c) 246 | || c == '-' 247 | || c == '+'; 248 | } 249 | 250 | private bool IsApostrophe(char c) 251 | { 252 | return c == '\''; 253 | } 254 | 255 | private bool IsBackslash(char c) 256 | { 257 | return c == '\\'; 258 | } 259 | 260 | private bool IsHash(char c) 261 | { 262 | return c == '#'; 263 | } 264 | 265 | private bool IsAt(char c) 266 | { 267 | return c == '@'; 268 | } 269 | 270 | private bool IsUpper(char c) 271 | { 272 | return c >= 'A' && c <= 'Z'; 273 | } 274 | 275 | private bool IsUpperOrDigit(char c) 276 | { 277 | return IsUpper(c) || IsDigit(c); 278 | } 279 | 280 | private bool IsEnumCharacter(char c) 281 | { 282 | return IsUpperOrDigit(c) || IsUnderscore(c); 283 | } 284 | 285 | private bool IsLeftParen(char c) 286 | { 287 | return c == '('; 288 | } 289 | 290 | private bool IsRightParen(char c) 291 | { 292 | return c == ')'; 293 | } 294 | 295 | private bool IsComma(char c) 296 | { 297 | return c == ','; 298 | } 299 | 300 | private bool IsKeywordCharacter(char c) 301 | { 302 | return IsUpperOrDigit(c) 303 | || IsUnderscore(c) 304 | || IsMinus(c); 305 | } 306 | 307 | private StepToken ParseNumber() 308 | { 309 | var tokenLine = _currentLineNumber; 310 | var tokenColumn = _currentColumn; 311 | var sb = new StringBuilder(); 312 | sb.Append(PeekCharacter()); 313 | Advance(); 314 | 315 | bool seenDecimal = false; 316 | bool seenE = false; 317 | char? cn; 318 | while ((cn = PeekCharacter()) != null) 319 | { 320 | var c = cn.GetValueOrDefault(); 321 | if (IsDigit(c)) 322 | { 323 | sb.Append(c); 324 | Advance(); 325 | } 326 | else if (IsDot(c) && !seenDecimal && !seenE) 327 | { 328 | sb.Append(c); 329 | seenDecimal = true; 330 | Advance(); 331 | } 332 | else if (IsE(c) && !seenE) 333 | { 334 | sb.Append(c); 335 | seenE = true; 336 | Advance(); 337 | } 338 | else if ((IsPlus(c) || IsMinus(c)) && seenE) 339 | { 340 | // TODO: this will fail on "1.0E+-+-+-+-1" 341 | sb.Append(c); 342 | Advance(); 343 | } 344 | else 345 | { 346 | break; 347 | } 348 | } 349 | 350 | var str = sb.ToString(); 351 | return seenDecimal || seenE 352 | ? (StepToken)new StepRealToken(double.Parse(str, CultureInfo.InvariantCulture), tokenLine, tokenColumn) 353 | : new StepIntegerToken(int.Parse(str, CultureInfo.InvariantCulture), tokenLine, tokenColumn); 354 | } 355 | 356 | private StepStringToken ParseString() 357 | { 358 | var tokenLine = _currentLineNumber; 359 | var tokenColumn = _currentColumn; 360 | var sb = new StringBuilder(); 361 | Advance(); 362 | 363 | char? cn; 364 | bool wasApostropheLast = false; 365 | bool wasBackslashLast = false; 366 | while ((cn = PeekCharacter()) != null) 367 | { 368 | var c = cn.GetValueOrDefault(); 369 | if (IsApostrophe(c) && wasApostropheLast) 370 | { 371 | // escaped 372 | sb.Append(c); 373 | Advance(); 374 | } 375 | else if (IsApostrophe(c) && !wasApostropheLast) 376 | { 377 | // maybe the end 378 | wasApostropheLast = true; 379 | Advance(); 380 | } 381 | else if (!IsApostrophe(c) && wasApostropheLast) 382 | { 383 | // end of string 384 | break; 385 | } 386 | else if (IsBackslash(c) && !wasBackslashLast) 387 | { 388 | // start escaping 389 | wasBackslashLast = true; 390 | Advance(); 391 | } 392 | else if (wasBackslashLast) 393 | { 394 | // TODO: handle real escaping 395 | sb.Append(c); 396 | Advance(); 397 | } 398 | else 399 | { 400 | // just a normal string 401 | sb.Append(c); 402 | Advance(); 403 | } 404 | } 405 | 406 | var str = sb.ToString(); 407 | return new StepStringToken(str, tokenLine, tokenColumn); 408 | } 409 | 410 | private StepToken ParseHashValue() 411 | { 412 | var tokenLine = _currentLineNumber; 413 | var tokenColumn = _currentColumn; 414 | Advance(); // swallow '#' 415 | var next = PeekCharacter(); 416 | if (next == null) 417 | { 418 | throw new StepReadException("Expected constant instance or entity instance", tokenLine, tokenColumn); 419 | } 420 | 421 | if (IsDigit(next.GetValueOrDefault())) 422 | { 423 | // entity instance: #1234 424 | return new StepEntityInstanceToken(int.Parse(TakeWhile(IsDigit), CultureInfo.InvariantCulture), tokenLine, tokenColumn); 425 | } 426 | else if (IsUpper(next.GetValueOrDefault())) 427 | { 428 | // constant instance: #INCH 429 | return new StepConstantInstanceToken(TakeWhile(IsUpperOrDigit), tokenLine, tokenColumn); 430 | } 431 | else 432 | { 433 | throw new StepReadException("Expected constant instance or entity instance", tokenLine, tokenColumn); 434 | } 435 | } 436 | 437 | private StepToken ParseAtValue() 438 | { 439 | var tokenLine = _currentLineNumber; 440 | var tokenColumn = _currentColumn; 441 | Advance(); // swallow '@' 442 | var next = PeekCharacter(); 443 | if (next == null) 444 | { 445 | throw new StepReadException("Expected constant value or instance value", tokenLine, tokenColumn); 446 | } 447 | 448 | if (IsDigit(next.GetValueOrDefault())) 449 | { 450 | // constant value: @PI 451 | return new StepConstantValueToken(TakeWhile(IsDigit), tokenLine, tokenColumn); 452 | } 453 | else if (IsUpper(next.GetValueOrDefault())) 454 | { 455 | // instance value: @12 456 | return new StepInstanceValueToken(int.Parse(TakeWhile(IsUpperOrDigit), CultureInfo.InvariantCulture), tokenLine, tokenColumn); 457 | } 458 | else 459 | { 460 | throw new StepReadException("Expected constant value or instance value", tokenLine, tokenColumn); 461 | } 462 | } 463 | 464 | private StepEnumerationToken ParseEnumeration() 465 | { 466 | var tokenLine = _currentLineNumber; 467 | var tokenColumn = _currentColumn; 468 | var sb = new StringBuilder(); 469 | Advance(); // swallow leading '.' 470 | var value = TakeWhile(IsEnumCharacter); 471 | if (string.IsNullOrEmpty(value)) 472 | { 473 | throw new StepReadException("Expected enumeration value", tokenLine, tokenColumn); 474 | } 475 | 476 | var next = PeekCharacter(); 477 | if (next.HasValue && IsDot(next.GetValueOrDefault())) 478 | { 479 | Advance(); 480 | return new StepEnumerationToken(value, tokenLine, tokenColumn); 481 | } 482 | else 483 | { 484 | throw new StepReadException("Expected enumeration ending dot", _currentLineNumber, _currentColumn); 485 | } 486 | } 487 | 488 | private StepKeywordToken ParseKeyword() 489 | { 490 | var tokenLine = _currentLineNumber; 491 | var tokenColumn = _currentColumn; 492 | var value = TakeWhile(IsKeywordCharacter); 493 | return new StepKeywordToken(value, tokenLine, tokenColumn); 494 | } 495 | 496 | private string TakeWhile(Func predicate) 497 | { 498 | var sb = new StringBuilder(); 499 | char? c; 500 | while ((c = PeekCharacter()) != null && predicate(c.GetValueOrDefault())) 501 | { 502 | sb.Append(c); 503 | Advance(); 504 | } 505 | 506 | return sb.ToString(); 507 | } 508 | } 509 | } 510 | -------------------------------------------------------------------------------- /src/IxMilia.Step/StepWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using IxMilia.Step.Items; 5 | using IxMilia.Step.Syntax; 6 | using IxMilia.Step.Tokens; 7 | 8 | namespace IxMilia.Step 9 | { 10 | internal class StepWriter 11 | { 12 | private StepFile _file; 13 | private int _currentLineLength; 14 | private bool _honorLineLength = true; 15 | private bool _inlineReferences; 16 | private Dictionary _itemMap; 17 | private int _nextId; 18 | 19 | private const int MaxLineLength = 80; 20 | 21 | public StepWriter(StepFile stepFile, bool inlineReferences) 22 | { 23 | _file = stepFile; 24 | _itemMap = new Dictionary(); 25 | _inlineReferences = inlineReferences; 26 | } 27 | 28 | public string GetContents() 29 | { 30 | var builder = new StringBuilder(); 31 | 32 | _honorLineLength = false; 33 | WriteDelimitedLine(StepFile.MagicHeader, builder); 34 | 35 | // output header 36 | WriteDelimitedLine(StepFile.HeaderText, builder); 37 | var headerSyntax = _file.GetHeaderSyntax(); 38 | foreach (var macro in headerSyntax.Macros) 39 | { 40 | WriteHeaderMacro(macro, builder); 41 | } 42 | 43 | WriteDelimitedLine(StepFile.EndSectionText, builder); 44 | 45 | _honorLineLength = true; 46 | 47 | // data section 48 | WriteDelimitedLine(StepFile.DataText, builder); 49 | foreach (var item in _file.Items) 50 | { 51 | WriteItem(item, builder); 52 | } 53 | 54 | WriteDelimitedLine(StepFile.EndSectionText, builder); 55 | WriteDelimitedLine(StepFile.MagicFooter, builder); 56 | 57 | return builder.ToString(); 58 | } 59 | 60 | private void WriteHeaderMacro(StepHeaderMacroSyntax macro, StringBuilder builder) 61 | { 62 | WriteText(macro.Name, builder); 63 | WriteTokens(macro.Values.GetTokens(), builder); 64 | WriteToken(StepSemicolonToken.Instance, builder); 65 | WriteNewLine(builder); 66 | } 67 | 68 | private int WriteItem(StepRepresentationItem item, StringBuilder builder) 69 | { 70 | if (!_inlineReferences) 71 | { 72 | // not inlining references, need to write out entities as we see them 73 | foreach (var referencedItem in item.GetReferencedItems()) 74 | { 75 | if (!_itemMap.ContainsKey(referencedItem)) 76 | { 77 | var refid = WriteItem(referencedItem, builder); 78 | } 79 | } 80 | } 81 | 82 | var id = ++_nextId; 83 | var syntax = GetItemSyntax(item, id); 84 | WriteToken(new StepEntityInstanceToken(id, -1, -1), builder); 85 | WriteToken(StepEqualsToken.Instance, builder); 86 | WriteTokens(syntax.GetTokens(), builder); 87 | WriteToken(StepSemicolonToken.Instance, builder); 88 | WriteNewLine(builder); 89 | return id; 90 | } 91 | 92 | /// 93 | /// Internal for testing. 94 | /// 95 | internal void WriteTokens(IEnumerable tokens, StringBuilder builder) 96 | { 97 | foreach (var token in tokens) 98 | { 99 | WriteToken(token, builder); 100 | } 101 | } 102 | 103 | private void WriteToken(StepToken token, StringBuilder builder) 104 | { 105 | WriteText(token.ToString(this), builder); 106 | } 107 | 108 | private void WriteDelimitedLine(string text, StringBuilder builder) 109 | { 110 | WriteText(text, builder); 111 | WriteToken(StepSemicolonToken.Instance, builder); 112 | WriteNewLine(builder); 113 | } 114 | 115 | private void WriteText(string text, StringBuilder builder) 116 | { 117 | if (_honorLineLength && _currentLineLength + text.Length > MaxLineLength) 118 | { 119 | WriteNewLine(builder); 120 | } 121 | 122 | builder.Append(text); 123 | _currentLineLength += text.Length; 124 | } 125 | 126 | private void WriteNewLine(StringBuilder builder) 127 | { 128 | builder.Append("\r\n"); 129 | _currentLineLength = 0; 130 | } 131 | 132 | private StepSyntax GetItemSyntax(StepRepresentationItem item, int expectedId) 133 | { 134 | if (!_itemMap.ContainsKey(item)) 135 | { 136 | var parameters = new StepSyntaxList(-1, -1, item.GetParameters(this)); 137 | var syntax = new StepSimpleItemSyntax(item.ItemType.GetItemTypeString(), parameters); 138 | _itemMap.Add(item, expectedId); 139 | return syntax; 140 | } 141 | else 142 | { 143 | return GetItemSyntax(item); 144 | } 145 | } 146 | 147 | public StepSyntax GetItemSyntax(StepRepresentationItem item) 148 | { 149 | if (_inlineReferences) 150 | { 151 | var parameters = new StepSyntaxList(-1, -1, item.GetParameters(this)); 152 | return new StepSimpleItemSyntax(item.ItemType.GetItemTypeString(), parameters); 153 | } 154 | else 155 | { 156 | return new StepEntityInstanceReferenceSyntax(_itemMap[item]); 157 | } 158 | } 159 | 160 | public StepSyntax GetItemSyntaxOrAuto(StepRepresentationItem item) 161 | { 162 | return item == null 163 | ? new StepAutoSyntax() 164 | : GetItemSyntax(item); 165 | } 166 | 167 | public static StepEnumerationValueSyntax GetBooleanSyntax(bool value) 168 | { 169 | var text = value ? "T" : "F"; 170 | return new StepEnumerationValueSyntax(text); 171 | } 172 | 173 | internal static IEnumerable SplitStringIntoParts(string str, int maxLength = 256) 174 | { 175 | var parts = new List(); 176 | if (str != null) 177 | { 178 | int offset = 0; 179 | while (offset < str.Length) 180 | { 181 | var length = Math.Min(maxLength, str.Length - offset); 182 | parts.Add(str.Substring(offset, length)); 183 | offset += length; 184 | } 185 | } 186 | else 187 | { 188 | parts.Add(string.Empty); 189 | } 190 | 191 | return parts; 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepAutoSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepAutoSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.Auto; 9 | 10 | public StepAsteriskToken Token { get; private set; } 11 | 12 | public StepAutoSyntax() 13 | : this(StepAsteriskToken.Instance) 14 | { 15 | } 16 | 17 | public StepAutoSyntax(StepAsteriskToken token) 18 | : base(token.Line, token.Column) 19 | { 20 | Token = token; 21 | } 22 | 23 | public override IEnumerable GetTokens() 24 | { 25 | yield return Token; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepComplexItemSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using IxMilia.Step.Tokens; 4 | 5 | namespace IxMilia.Step.Syntax 6 | { 7 | internal class StepComplexItemSyntax : StepItemSyntax 8 | { 9 | public override StepSyntaxType SyntaxType => StepSyntaxType.ComplexItem; 10 | 11 | public List Items { get; } = new List(); 12 | 13 | public StepComplexItemSyntax(int line, int column, IEnumerable items) 14 | : base(line, column) 15 | { 16 | Items = items.ToList(); 17 | } 18 | 19 | public override IEnumerable GetTokens() 20 | { 21 | return Items.SelectMany(i => i.GetTokens()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepDataSectionSyntax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IxMilia.Step.Tokens; 5 | 6 | namespace IxMilia.Step.Syntax 7 | { 8 | internal class StepDataSectionSyntax : StepSyntax 9 | { 10 | public override StepSyntaxType SyntaxType => StepSyntaxType.DataSection; 11 | 12 | public List ItemInstances { get; } 13 | 14 | public StepDataSectionSyntax(int line, int column, IEnumerable itemInstances) 15 | : base(line, column) 16 | { 17 | ItemInstances = itemInstances.ToList(); 18 | } 19 | 20 | public override IEnumerable GetTokens() 21 | { 22 | throw new NotSupportedException(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepEntityInstanceReferenceSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepEntityInstanceReferenceSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.EntityInstanceReference; 9 | 10 | public int Id { get; } 11 | 12 | public StepEntityInstanceReferenceSyntax(int id) 13 | : base(-1, -1) 14 | { 15 | Id = id; 16 | } 17 | 18 | public StepEntityInstanceReferenceSyntax(StepEntityInstanceToken itemInstance) 19 | : base(itemInstance.Line, itemInstance.Column) 20 | { 21 | Id = itemInstance.Id; 22 | } 23 | 24 | public override IEnumerable GetTokens() 25 | { 26 | yield return new StepEntityInstanceToken(Id, -1, -1); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepEntityInstanceSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepEntityInstanceSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.EntityInstance; 9 | 10 | public int Id { get; } 11 | public StepItemSyntax SimpleItemInstance { get; } 12 | 13 | public StepEntityInstanceSyntax(StepEntityInstanceToken instanceId, StepItemSyntax itemInstance) 14 | : base(instanceId.Line, instanceId.Column) 15 | { 16 | Id = instanceId.Id; 17 | SimpleItemInstance = itemInstance; 18 | } 19 | 20 | public override IEnumerable GetTokens() 21 | { 22 | yield return new StepEntityInstanceToken(Id, -1, -1); 23 | foreach (var token in SimpleItemInstance.GetTokens()) 24 | { 25 | yield return token; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepEnumerationValueSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepEnumerationValueSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.Enumeration; 9 | 10 | public string Value { get; } 11 | 12 | public StepEnumerationValueSyntax(string value) 13 | : base(-1, -1) 14 | { 15 | Value = value; 16 | } 17 | 18 | public StepEnumerationValueSyntax(StepEnumerationToken value) 19 | : base(value.Line, value.Column) 20 | { 21 | Value = value.Value; 22 | } 23 | 24 | public override IEnumerable GetTokens() 25 | { 26 | yield return new StepEnumerationToken(Value, -1, -1); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepFileSyntax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Tokens; 4 | 5 | namespace IxMilia.Step.Syntax 6 | { 7 | internal class StepFileSyntax : StepSyntax 8 | { 9 | public override StepSyntaxType SyntaxType => StepSyntaxType.File; 10 | 11 | public StepHeaderSectionSyntax Header { get; } 12 | public StepDataSectionSyntax Data { get; } 13 | 14 | public StepFileSyntax(StepHeaderSectionSyntax header, StepDataSectionSyntax data) 15 | : base(header.Line, header.Column) 16 | { 17 | Header = header; 18 | Data = data; 19 | } 20 | 21 | public override IEnumerable GetTokens() 22 | { 23 | throw new NotSupportedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepHeaderMacroSyntax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using IxMilia.Step.Tokens; 4 | 5 | namespace IxMilia.Step.Syntax 6 | { 7 | internal class StepHeaderMacroSyntax : StepSyntax 8 | { 9 | public override StepSyntaxType SyntaxType => StepSyntaxType.HeaderMacro; 10 | 11 | public string Name { get; } 12 | public StepSyntaxList Values { get; } 13 | 14 | public StepHeaderMacroSyntax(string name, StepSyntaxList values) 15 | : base(values.Line, values.Column) 16 | { 17 | Name = name; 18 | Values = values; 19 | } 20 | 21 | public override IEnumerable GetTokens() 22 | { 23 | throw new NotSupportedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepHeaderSectionSyntax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IxMilia.Step.Tokens; 5 | 6 | namespace IxMilia.Step.Syntax 7 | { 8 | internal class StepHeaderSectionSyntax : StepSyntax 9 | { 10 | public override StepSyntaxType SyntaxType => StepSyntaxType.HeaderSection; 11 | 12 | public List Macros { get; } 13 | 14 | public StepHeaderSectionSyntax(int line, int column, IEnumerable macros) 15 | : base(line, column) 16 | { 17 | Macros = macros.ToList(); 18 | } 19 | 20 | public override IEnumerable GetTokens() 21 | { 22 | throw new NotSupportedException(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepIntegerSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepIntegerSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.Integer; 9 | 10 | public int Value { get; } 11 | 12 | public StepIntegerSyntax( int value ) 13 | : base(-1, -1) 14 | { 15 | Value = value; 16 | } 17 | 18 | public StepIntegerSyntax(StepIntegerToken value) 19 | : base(value.Line, value.Column) 20 | { 21 | Value = value.Value; 22 | } 23 | 24 | public override IEnumerable GetTokens() 25 | { 26 | yield return new StepIntegerToken(Value, -1, -1); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepItemSyntax.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Syntax 2 | { 3 | internal abstract class StepItemSyntax : StepSyntax 4 | { 5 | protected StepItemSyntax(int line, int column) 6 | : base(line, column) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepOmittedSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepOmittedSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.Omitted; 9 | 10 | public StepOmittedSyntax(StepOmittedToken value) 11 | : base(value.Line, value.Column) 12 | { 13 | } 14 | 15 | public override IEnumerable GetTokens() 16 | { 17 | yield return StepOmittedToken.Instance; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepRealSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepRealSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.Real; 9 | 10 | public double Value { get; } 11 | 12 | public StepRealSyntax(double value) 13 | : base(-1, -1) 14 | { 15 | Value = value; 16 | } 17 | 18 | public StepRealSyntax(StepRealToken value) 19 | : base(value.Line, value.Column) 20 | { 21 | Value = value.Value; 22 | } 23 | 24 | public override IEnumerable GetTokens() 25 | { 26 | yield return new StepRealToken(Value, -1, -1); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepSimpleItemSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepSimpleItemSyntax : StepItemSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.SimpleItem; 9 | 10 | public string Keyword { get; } 11 | public StepSyntaxList Parameters { get; } 12 | 13 | public StepSimpleItemSyntax(string keyword, StepSyntaxList parameters) 14 | : base(-1, -1) 15 | { 16 | Keyword = keyword; 17 | Parameters = parameters; 18 | } 19 | 20 | public StepSimpleItemSyntax(StepKeywordToken keyword, StepSyntaxList parameters) 21 | : base(keyword.Line, keyword.Column) 22 | { 23 | Keyword = keyword.Value; 24 | Parameters = parameters; 25 | } 26 | 27 | public override IEnumerable GetTokens() 28 | { 29 | yield return new StepKeywordToken(Keyword, -1, -1); 30 | foreach (var token in Parameters.GetTokens()) 31 | { 32 | yield return token; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepStringSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal class StepStringSyntax : StepSyntax 7 | { 8 | public override StepSyntaxType SyntaxType => StepSyntaxType.String; 9 | 10 | public string Value { get; } 11 | 12 | public StepStringSyntax(string value) 13 | : base(-1, -1) 14 | { 15 | Value = value; 16 | } 17 | 18 | public StepStringSyntax(StepStringToken value) 19 | : base(value.Line, value.Column) 20 | { 21 | Value = value.Value; 22 | } 23 | 24 | public override IEnumerable GetTokens() 25 | { 26 | yield return new StepStringToken(Value, -1, -1); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepSyntax.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IxMilia.Step.Tokens; 3 | 4 | namespace IxMilia.Step.Syntax 5 | { 6 | internal abstract class StepSyntax 7 | { 8 | public abstract StepSyntaxType SyntaxType { get; } 9 | 10 | public int Line { get; } 11 | public int Column { get; } 12 | 13 | protected StepSyntax(int line, int column) 14 | { 15 | Line = line; 16 | Column = column; 17 | } 18 | 19 | public abstract IEnumerable GetTokens(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepSyntaxExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | namespace IxMilia.Step.Syntax 6 | { 7 | internal static class StepSyntaxExtensions 8 | { 9 | public static readonly string[] DateTimeFormats = new string[] 10 | { 11 | "yyyy-MM-ddT", 12 | "yyyy-M-dTh:mm:ss ttzzz", 13 | }; 14 | 15 | public static void AssertListCount(this StepSyntaxList syntaxList, int count) 16 | { 17 | if (syntaxList.Values.Count != count) 18 | { 19 | ReportError($"Expected list to contain {count} items but it contained {syntaxList.Values.Count}", syntaxList); 20 | } 21 | } 22 | 23 | public static void AssertListCount(this StepSyntaxList syntaxList, int minCount, int maxCount) 24 | { 25 | if (syntaxList.Values.Count < minCount || syntaxList.Values.Count > maxCount) 26 | { 27 | ReportError($"Expected list to contain between {minCount} and {maxCount} items but it contained {syntaxList.Values.Count}", syntaxList); 28 | } 29 | } 30 | 31 | public static string GetStringValue(this StepSyntax syntax) 32 | { 33 | switch (syntax.SyntaxType) 34 | { 35 | case StepSyntaxType.Omitted: 36 | return string.Empty; 37 | case StepSyntaxType.String: 38 | return ((StepStringSyntax)syntax).Value; 39 | default: 40 | ReportError("Expected string value", syntax); 41 | return null; // this will never get here because `ReportError` throws 42 | } 43 | } 44 | 45 | public static DateTime GetDateTimeValue(this StepSyntax syntax) 46 | { 47 | var str = syntax.GetStringValue(); 48 | DateTime result; 49 | if (DateTime.TryParseExact(str, DateTimeFormats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result)) 50 | { 51 | return result; 52 | } 53 | else 54 | { 55 | return DateTime.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal); 56 | } 57 | } 58 | 59 | public static double GetRealVavlue(this StepSyntax syntax) 60 | { 61 | if (syntax.SyntaxType != StepSyntaxType.Real) 62 | { 63 | ReportError("Expected real value", syntax); 64 | } 65 | 66 | return ((StepRealSyntax)syntax).Value; 67 | } 68 | 69 | public static int GetIntegerValue(this StepSyntax syntax) 70 | { 71 | if (syntax.SyntaxType != StepSyntaxType.Integer) 72 | { 73 | ReportError("Expected integer value", syntax); 74 | } 75 | 76 | return ((StepIntegerSyntax)syntax).Value; 77 | } 78 | 79 | public static string GetEnumerationValue(this StepSyntax syntax) 80 | { 81 | if (syntax.SyntaxType != StepSyntaxType.Enumeration) 82 | { 83 | ReportError("Expected enumeration value", syntax); 84 | } 85 | 86 | return ((StepEnumerationValueSyntax)syntax).Value; 87 | } 88 | 89 | public static bool GetBooleanValue(this StepSyntax syntax) 90 | { 91 | switch (syntax.GetEnumerationValue().ToUpperInvariant()) 92 | { 93 | case "T": 94 | case "TRUE": 95 | return true; 96 | case "F": 97 | case "FALSE": 98 | return false; 99 | default: 100 | ReportError("Expected boolean value", syntax); 101 | return false; // unreachable 102 | } 103 | } 104 | 105 | public static double GetRealValueOrDefault(this StepSyntaxList syntaxList, int index) 106 | { 107 | return syntaxList.GetRealValueOrDefault(index, 0.0); 108 | } 109 | 110 | public static double GetRealValueOrDefault(this StepSyntaxList syntaxList, int index, double defaultValue) 111 | { 112 | if (index < 0) 113 | { 114 | throw new ArgumentOutOfRangeException(nameof(index)); 115 | } 116 | 117 | if (index < syntaxList.Values.Count) 118 | { 119 | return syntaxList.Values[index].GetRealVavlue(); 120 | } 121 | else 122 | { 123 | return defaultValue; 124 | } 125 | } 126 | 127 | public static StepSyntaxList GetValueList(this StepSyntax syntax) 128 | { 129 | if (syntax.SyntaxType != StepSyntaxType.List) 130 | { 131 | ReportError("Expected list value", syntax); 132 | } 133 | 134 | return (StepSyntaxList)syntax; 135 | } 136 | 137 | public static string GetConcatenatedStringValue(this StepSyntax syntax) 138 | { 139 | return string.Join(string.Empty, syntax.GetValueList().Values.Select(v => v.GetStringValue())); 140 | } 141 | 142 | private static void ReportError(string message, StepSyntax location) 143 | { 144 | ReportError(message, location.Line, location.Column); 145 | } 146 | 147 | private static void ReportError(string message, int line, int column) 148 | { 149 | throw new StepReadException(message, line, column); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepSyntaxList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using IxMilia.Step.Tokens; 4 | 5 | namespace IxMilia.Step.Syntax 6 | { 7 | internal class StepSyntaxList : StepSyntax 8 | { 9 | public override StepSyntaxType SyntaxType => StepSyntaxType.List; 10 | 11 | public List Values { get; } 12 | 13 | public StepSyntaxList(params StepSyntax[] values) 14 | : this(-1, -1, values) 15 | { 16 | } 17 | 18 | public StepSyntaxList(IEnumerable values) 19 | : this(-1, -1, values) 20 | { 21 | } 22 | 23 | public StepSyntaxList(int line, int column, IEnumerable values) 24 | : base(line, column) 25 | { 26 | Values = values.ToList(); 27 | } 28 | 29 | public override IEnumerable GetTokens() 30 | { 31 | yield return StepLeftParenToken.Instance; 32 | for (int i = 0; i < Values.Count; i++) 33 | { 34 | foreach (var token in Values[i].GetTokens()) 35 | { 36 | yield return token; 37 | } 38 | 39 | if (i < Values.Count - 1) 40 | { 41 | yield return StepCommaToken.Instance; 42 | } 43 | } 44 | 45 | yield return StepRightParenToken.Instance; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Syntax/StepSyntaxType.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Syntax 2 | { 3 | internal enum StepSyntaxType 4 | { 5 | File, 6 | HeaderSection, 7 | DataSection, 8 | HeaderMacro, 9 | SimpleItem, 10 | ComplexItem, 11 | EntityInstance, 12 | EntityInstanceReference, 13 | Integer, 14 | Real, 15 | List, 16 | String, 17 | Auto, 18 | Enumeration, 19 | Omitted 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepAsteriskToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepAsteriskToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Asterisk; 6 | 7 | public StepAsteriskToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return "*"; 15 | } 16 | 17 | public static StepAsteriskToken Instance { get; } = new StepAsteriskToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepCommaToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepCommaToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Comma; 6 | 7 | public StepCommaToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return ","; 15 | } 16 | 17 | public static StepCommaToken Instance { get; } = new StepCommaToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepConstantInstanceToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepConstantInstanceToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.ConstantInstance; 6 | 7 | public string Name { get; } 8 | 9 | public StepConstantInstanceToken(string name, int line, int column) 10 | : base(line, column) 11 | { 12 | Name = name; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return "#" + Name; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepConstantValueToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepConstantValueToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.ConstantValue; 6 | 7 | public string Name { get; } 8 | 9 | public StepConstantValueToken(string name, int line, int column) 10 | : base(line, column) 11 | { 12 | Name = name; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return "@" + Name; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepEntityInstanceToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepEntityInstanceToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.EntityInstance; 6 | 7 | public int Id { get; } 8 | 9 | public StepEntityInstanceToken(int id, int line, int column) 10 | : base(line, column) 11 | { 12 | Id = id; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return "#" + Id.ToString(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepEnumerationToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepEnumerationToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Enumeration; 6 | 7 | public string Value { get; } 8 | 9 | public StepEnumerationToken(string value, int line, int column) 10 | : base(line, column) 11 | { 12 | Value = value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return "." + Value + "."; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepEqualsToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepEqualsToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Equals; 6 | 7 | public StepEqualsToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return "="; 15 | } 16 | 17 | public static StepEqualsToken Instance { get; } = new StepEqualsToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepInstanceValueToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepInstanceValueToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.InstanceValue; 6 | 7 | public int Id { get; } 8 | 9 | public StepInstanceValueToken(int id, int line, int column) 10 | : base(line, column) 11 | { 12 | Id = id; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return "@" + Id.ToString(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepIntegerToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepIntegerToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Integer; 6 | 7 | public int Value { get; } 8 | 9 | public StepIntegerToken(int value, int line, int column) 10 | : base(line, column) 11 | { 12 | Value = value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return Value.ToString(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepKeywordToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepKeywordToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Keyword; 6 | 7 | public string Value { get; } 8 | 9 | public StepKeywordToken(string value, int line, int column) 10 | : base(line, column) 11 | { 12 | Value = value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return Value; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepLeftParenToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepLeftParenToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.LeftParen; 6 | 7 | public StepLeftParenToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return "("; 15 | } 16 | 17 | public static StepLeftParenToken Instance { get; } = new StepLeftParenToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepOmittedToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepOmittedToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Omitted; 6 | 7 | public StepOmittedToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return "$"; 15 | } 16 | 17 | public static StepOmittedToken Instance { get; } = new StepOmittedToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepRealToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepRealToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Real; 6 | 7 | public double Value { get; } 8 | 9 | public StepRealToken(double value, int line, int column) 10 | : base(line, column) 11 | { 12 | Value = value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return Value.ToString("0.0#"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepRightParenToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepRightParenToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.RightParen; 6 | 7 | public StepRightParenToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return ")"; 15 | } 16 | 17 | public static StepRightParenToken Instance { get; } = new StepRightParenToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepSemiColonToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepSemicolonToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.Semicolon; 6 | 7 | public StepSemicolonToken(int line, int column) 8 | : base(line, column) 9 | { 10 | } 11 | 12 | public override string ToString() 13 | { 14 | return ";"; 15 | } 16 | 17 | public static StepSemicolonToken Instance { get; } = new StepSemicolonToken(-1, -1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepStringToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal class StepStringToken : StepToken 4 | { 5 | public override StepTokenKind Kind => StepTokenKind.String; 6 | 7 | public string Value { get; } 8 | 9 | public StepStringToken(string value, int line, int column) 10 | : base(line, column) 11 | { 12 | Value = value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | // TODO: escaping 18 | return "'" + Value + "'"; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepToken.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal abstract class StepToken 4 | { 5 | public abstract StepTokenKind Kind { get; } 6 | 7 | public int Line { get; } 8 | public int Column { get; } 9 | 10 | protected StepToken(int line, int column) 11 | { 12 | Line = line; 13 | Column = column; 14 | } 15 | 16 | public virtual string ToString(StepWriter writer) 17 | { 18 | return ToString(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/IxMilia.Step/Tokens/StepTokenKind.cs: -------------------------------------------------------------------------------- 1 | namespace IxMilia.Step.Tokens 2 | { 3 | internal enum StepTokenKind 4 | { 5 | Semicolon, 6 | Omitted, 7 | Integer, 8 | Real, 9 | String, 10 | ConstantInstance, 11 | ConstantValue, 12 | EntityInstance, 13 | InstanceValue, 14 | Enumeration, 15 | LeftParen, 16 | RightParen, 17 | Comma, 18 | Equals, 19 | Asterisk, 20 | Keyword 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/create-package.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set PROJECT_NAME=IxMilia.Step 4 | set CONFIGURATION=Release 5 | set PROJECT=%~dp0\%PROJECT_NAME%\%PROJECT_NAME%.csproj 6 | 7 | dotnet restore "%PROJECT%" 8 | if errorlevel 1 exit /b 1 9 | 10 | dotnet build "%PROJECT%" --configuration %CONFIGURATION% 11 | if errorlevel 1 exit /b 1 12 | 13 | dotnet pack --no-restore --no-build --configuration %CONFIGURATION% "%PROJECT%" 14 | if errorlevel 1 exit /b 1 15 | -------------------------------------------------------------------------------- /src/create-package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | _SCRIPT_DIR="$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )" 4 | PROJECT_NAME=IxMilia.Step 5 | CONFIGURATION=Release 6 | PROJECT=$_SCRIPT_DIR/$PROJECT_NAME/$PROJECT_NAME.csproj 7 | 8 | dotnet restore "$PROJECT" 9 | dotnet build "$PROJECT" --configuration $CONFIGURATION 10 | dotnet pack --no-restore --no-build --configuration $CONFIGURATION "$PROJECT" 11 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 0.1.0 2 | --------------------------------------------------------------------------------