diff options
| author | sekelsenmat <sekelsenmat@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2011-01-26 09:11:53 +0000 |
|---|---|---|
| committer | sekelsenmat <sekelsenmat@3ad0048d-3df7-0310-abae-a5850022a9f2> | 2011-01-26 09:11:53 +0000 |
| commit | d36b6c557f8e24826017c6d80cb2acf181ea7712 (patch) | |
| tree | fbde75d1286677a2f6dbd2a463e0d237a35135e5 /packages/fpvectorial/src/dxfvectorialreader.pas | |
| parent | 4fc96fef4fd380db657eb27085f6e066f881b2cb (diff) | |
| download | fpc-d36b6c557f8e24826017c6d80cb2acf181ea7712.tar.gz | |
Large improvements to the DXF reader and adds a new circle entity to fpvectorial
git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@16816 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'packages/fpvectorial/src/dxfvectorialreader.pas')
| -rw-r--r-- | packages/fpvectorial/src/dxfvectorialreader.pas | 557 |
1 files changed, 406 insertions, 151 deletions
diff --git a/packages/fpvectorial/src/dxfvectorialreader.pas b/packages/fpvectorial/src/dxfvectorialreader.pas index a983332b64..ca3d7fd6db 100644 --- a/packages/fpvectorial/src/dxfvectorialreader.pas +++ b/packages/fpvectorial/src/dxfvectorialreader.pas @@ -17,7 +17,7 @@ SECTION_NAME ENDSEC 0 -after all section end there is: +after all sections there is: EOF @@ -37,20 +37,53 @@ type { Used by tcutils.SeparateString } T10Strings = array[0..9] of shortstring; + TDXFToken = class; + + TDXFTokens = TFPList;// TDXFToken; + + TDXFToken = class + GroupCode: Integer; + StrValue: string; + FloatValue: double; + IntValue: Integer; + Childs: TDXFTokens; + constructor Create; + Destructor Destroy; override; + end; + + { TDXFTokenizer } + + TDXFTokenizer = class + public + Tokens: TDXFTokens; + constructor Create; + Destructor Destroy; override; + procedure ReadFromStrings(AStrings: TStrings); + function IsENTITIES_Subsection(AStr: string): Boolean; + end; + { TvDXFVectorialReader } TvDXFVectorialReader = class(TvCustomVectorialReader) private + // CIRCLE + CircleCenterX, CircleCenterY, CircleCenterZ, CircleRadius: Double; + // LINE LineStartX, LineStartY, LineStartZ: Double; LineEndX, LineEndY, LineEndZ: Double; + // function SeparateString(AString: string; ASeparator: Char): T10Strings; - function ReadSection(AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean; - function ReadENTITIES(AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean; - function ReadENTITIES_LINE(AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean; - function GetCoordinate(AStr: shortstring): Integer; + procedure ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument); + procedure ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument); + procedure ReadENTITIES_CIRCLE(ATokens: TDXFTokens; AData: TvVectorialDocument); + procedure ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; AData: TvVectorialDocument); + procedure ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument); function GetCoordinateValue(AStr: shortstring): Double; public { General reading methods } + Tokenizer: TDXFTokenizer; + constructor Create; override; + Destructor Destroy; override; procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); override; end; @@ -59,23 +92,190 @@ implementation {$define FPVECTORIALDEBUG} const - { Coordinate constants } + // Group Codes for ENTITIES + DXF_ENTITIES_TYPE = 0; + DXF_ENTITIES_HANDLE = 5; + DXF_ENTITIES_APPLICATION_GROUP = 102; + DXF_ENTITIES_AcDbEntity = 100; + DXF_ENTITIES_MODEL_OR_PAPER_SPACE = 67; // default=0=model, 1=paper + DXF_ENTITIES_VISIBILITY = 60; // default=0 = Visible, 1 = Invisible - INT_COORDINATE_NONE = 0; - INT_COORDINATE_X = 1; - INT_COORDINATE_Y = 2; - INT_COORDINATE_Z = 3; +{ TDXFToken } + +constructor TDXFToken.Create; +begin + inherited Create; + + Childs := TDXFTokens.Create; +end; + +destructor TDXFToken.Destroy; +begin + Childs.Free; + + inherited Destroy; +end; + +{ TDXFTokenizer } + +constructor TDXFTokenizer.Create; +begin + inherited Create; + + Tokens := TDXFTokens.Create; +end; + +destructor TDXFTokenizer.Destroy; +begin + Tokens.Free; + + inherited Destroy; +end; + +procedure TDXFTokenizer.ReadFromStrings(AStrings: TStrings); +var + i: Integer; + StrSectionGroupCode, StrSectionName: string; + IntSectionGroupCode: Integer; + CurTokenBase, NextTokenBase, SectionTokenBase: TDXFTokens; + NewToken: TDXFToken; + ParserState: Integer; +begin + // Tokens.ForEachCall(); deletecallback + Tokens.Clear; + + CurTokenBase := Tokens; + NextTokenBase := Tokens; + i := 0; + ParserState := 0; - { GCode constants } + while i < AStrings.Count - 1 do + begin + CurTokenBase := NextTokenBase; + + // Now read and process the section name + StrSectionGroupCode := AStrings.Strings[i]; + IntSectionGroupCode := StrToInt(Trim(StrSectionGroupCode)); + StrSectionName := AStrings.Strings[i+1]; + + NewToken := TDXFToken.Create; + NewToken.GroupCode := IntSectionGroupCode; + NewToken.StrValue := StrSectionName; + + // Waiting for a section + if ParserState = 0 then + begin + if (StrSectionName = 'SECTION') then + begin + ParserState := 1; + NextTokenBase := NewToken.Childs; + end + else if (StrSectionName = 'EOF') then + begin + Exit; + end + else + begin + raise Exception.Create(Format( + 'TDXFTokenizer.ReadFromStrings: Expected SECTION, but got: %s', [StrSectionname])); + end; + end + // Processing the section name + else if ParserState = 1 then + begin + if (StrSectionName = 'HEADER') or + (StrSectionName = 'CLASSES') or + (StrSectionName = 'TABLES') or + (StrSectionName = 'BLOCKS') or + (StrSectionName = 'OBJECTS') or + (StrSectionName = 'THUMBNAILIMAGE') then + begin + ParserState := 2; + SectionTokenBase := CurTokenBase; + end + else if (StrSectionName = 'ENTITIES') then + begin + ParserState := 3; + SectionTokenBase := CurTokenBase; + end + else + begin + raise Exception.Create(Format( + 'TDXFTokenizer.ReadFromStrings: Invalid section name: %s', [StrSectionname])); + end; + end + // Reading a generic section + else if ParserState = 2 then + begin + if StrSectionName = 'ENDSEC' then + begin + ParserState := 0; + CurTokenBase := SectionTokenBase; + NextTokenBase := Tokens; + end; + end + // Reading the ENTITIES section + else if ParserState = 3 then + begin + if IsENTITIES_Subsection(StrSectionName) then + begin + CurTokenBase := SectionTokenBase; + NextTokenBase := NewToken.Childs; + end + else if StrSectionName = 'ENDSEC' then + begin + ParserState := 0; + CurTokenBase := SectionTokenBase; + NextTokenBase := Tokens; + end; + end; + + CurTokenBase.Add(NewToken); + + Inc(i, 2); + end; +end; - STR_GCODE_LINEAR_MOVE = 'G01'; - STR_GCODE_STEPPER_MOVE = 'S01'; - STR_GCODE_2DBEZIER_MOVE = 'B02'; - STR_GCODE_3DBEZIER_MOVE = 'B03'; - STR_GCODE_DRILL_UP = 'P01'; - STR_GCODE_DRILL_DOWN = 'P02'; +function TDXFTokenizer.IsENTITIES_Subsection(AStr: string): Boolean; +begin + Result := + (AStr = '3DFACE') or + (AStr = '3DSOLID') or + (AStr = 'ACAD_PROXY_ENTITY') or + (AStr = 'ARC') or + (AStr = 'ATTDEF') or + (AStr = 'ATTRIB') or + (AStr = 'BODY') or + (AStr = 'CIRCLE') or + (AStr = 'DIMENSION') or + (AStr = 'ELLIPSE') or + (AStr = 'HATCH') or + (AStr = 'IMAGE') or + (AStr = 'INSERT') or + (AStr = 'LEADER') or + (AStr = 'LINE') or + (AStr = 'LWPOLYLINE') or + (AStr = 'MLINE') or + (AStr = 'MTEXT') or + (AStr = 'OLEFRAME') or + (AStr = 'OLE2FRAME') or + (AStr = 'POINT') or + (AStr = 'POLYLINE') or + (AStr = 'RAY') or + (AStr = 'REGION') or + (AStr = 'SEQEND') or + (AStr = 'SHAPE') or + (AStr = 'SOLID') or + (AStr = 'SPLINE') or + (AStr = 'TEXT') or + (AStr = 'TOLERANCE') or + (AStr = 'TRACE') or + (AStr = 'VERTEX') or + (AStr = 'VIEWPORT') or + (AStr = 'XLINE'); +end; -{ TvAvisoCNCGCodeReader } +{ TvDXFVectorialReader } {@@ Reads a string and separates it in substring @@ -110,172 +310,219 @@ begin end; end; -{@@ - returns If an end of file marker was found -} -function TvDXFVectorialReader.ReadSection( - AStrings: TStrings; var AIndex: Integer; AData: TvVectorialDocument): Boolean; +procedure TvDXFVectorialReader.ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument); var - DestX, DestY, DestZ: Double; - StrSectionNum, StrSectionName: string; - IntSectionNum, i: Integer; + i: Integer; + CurToken: TDXFToken; begin - Result := False; - - // Check if there is minimal space for a section - if AIndex+5 > AStrings.Count then + for i := 0 to ATokens.Count - 1 do begin - {$ifdef FPVECTORIALDEBUG} - WriteLn('Not enough space for a section'); - {$endif} - Exit(True); - end; + CurToken := TDXFToken(ATokens.Items[i]); + if CurToken.StrValue = 'CIRCLE' then + begin + CircleCenterX := 0.0; + CircleCenterY := 0.0; + CircleCenterZ := 0.0; + CircleRadius := 0.0; - // Check of the EOF marker - StrSectionName := Trim(AStrings.Strings[AIndex+1]); - if StrSectionName = 'EOF' then - begin - {$ifdef FPVECTORIALDEBUG} - WriteLn('EOF found'); - {$endif} - Exit(True); - end; + ReadENTITIES_CIRCLE(CurToken.Childs, AData); - // Now read and process the section name - StrSectionNum := AStrings.Strings[AIndex+2]; - IntSectionNum := StrToInt(Trim(StrSectionNum)); - StrSectionName := AStrings.Strings[AIndex+3]; - - {$ifdef FPVECTORIALDEBUG} - WriteLn('TvDXFVectorialReader.ReadSection ' + StrSectionName); - {$endif} - - if (StrSectionName = 'HEADER') or - (StrSectionName = 'CLASSES') or - (StrSectionName = 'TABLES') or - (StrSectionName = 'BLOCKS') or - (StrSectionName = 'OBJECTS') or - (StrSectionName = 'THUMBNAILIMAGE') then - begin - // We don't care about contents here, so let's just find the last section and get out of here. - for i := AIndex + 4 to AStrings.Count - 1 do + AData.AddCircle(CircleCenterX, CircleCenterY, + CircleCenterZ, CircleRadius); + end + else if CurToken.StrValue = 'ELLIPSE' then begin - if AStrings.Strings[i] = 'ENDSEC' then - begin - AIndex := i + 1; - Exit; - end; + // ... + ReadENTITIES_ELLIPSE(CurToken.Childs, AData); + end + else if CurToken.StrValue = 'LINE' then + begin + // Initial values + LineStartX := 0; + LineStartY := 0; + LineStartZ := 0; + LineEndX := 0; + LineEndY := 0; + LineEndZ := 0; + + // Read the data of the line + ReadENTITIES_LINE(CurToken.Childs, AData); + + // And now write it + {$ifdef FPVECTORIALDEBUG} + WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY])); + {$endif} + AData.StartPath(LineStartX, LineStartY); + AData.AddLineToPath(LineEndX, LineEndY); + AData.EndPath(); + end + else if CurToken.StrValue = 'TEXT' then + begin + // ... end; - // If we reached here, the section in incomplete - raise Exception.Create('TvDXFVectorialReader.ReadSection: ENDSEC was not found in the SECTION'); - end - else if StrSectionName = 'ENTITIES' then - begin - AIndex := AIndex + 4; - while not ReadENTITIES(AStrings, AIndex, AData) do ; end; - {else +end; + +procedure TvDXFVectorialReader.ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument); +var + CurToken: TDXFToken; + i: Integer; +begin + for i := 0 to ATokens.Count - 1 do begin - end;} + // Now read and process the item name + CurToken := TDXFToken(ATokens.Items[i]); + + // Avoid an exception by previously checking if the conversion can be made + if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or + (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue; + + CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue)); + + case CurToken.GroupCode of + 10: LineStartX := CurToken.FloatValue; + 20: LineStartY := CurToken.FloatValue; + 30: LineStartZ := CurToken.FloatValue; + 11: LineEndX := CurToken.FloatValue; + 21: LineEndY := CurToken.FloatValue; + 31: LineEndZ := CurToken.FloatValue; + end; + end; end; -function TvDXFVectorialReader.ReadENTITIES(AStrings: TStrings; - var AIndex: Integer; AData: TvVectorialDocument): Boolean; +{ +Group codes Description +100 Subclass marker (AcDbCircle) +39 Thickness (optional; default = 0) +10 Center point (in OCS) DXF: X value; APP: 3D point +20, 30 DXF: Y and Z values of center point (in OCS) +40 Radius +210 Extrusion direction (optional; default = 0, 0, 1) DXF: X value; APP: 3D vector +220, 230 DXF: Y and Z values of extrusion direction (optional) +} +procedure TvDXFVectorialReader.ReadENTITIES_CIRCLE(ATokens: TDXFTokens; + AData: TvVectorialDocument); var - StrSectionNum, StrSectionName: string; - IntSectionNum, i: Integer; + CurToken: TDXFToken; + i: Integer; begin - Result := False; + for i := 0 to ATokens.Count - 1 do + begin + // Now read and process the item name + CurToken := TDXFToken(ATokens.Items[i]); - // Now read and process the item name - StrSectionName := AStrings.Strings[AIndex+1]; + // Avoid an exception by previously checking if the conversion can be made + if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or + (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue; - {$ifdef FPVECTORIALDEBUG} - WriteLn('TvDXFVectorialReader.ReadENTITIES ', StrSectionName); - {$endif} + CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue)); - if StrSectionName = 'ENDSEC' then - begin - Inc(AIndex, 2); - Exit(True); - end - else if StrSectionName = 'LINE' then - begin - // Initial values - LineStartX := 0; - LineStartY := 0; - LineStartZ := 0; - LineEndX := 0; - LineEndY := 0; - LineEndZ := 0; - - // Read the data of the line - Inc(AIndex, 2); - while not ReadENTITIES_LINE(AStrings, AIndex, AData) do ; - - // And now write it - {$ifdef FPVECTORIALDEBUG} - WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY])); - {$endif} - AData.StartPath(LineStartX, LineStartY); - AData.AddLineToPath(LineEndX, LineEndY); - AData.EndPath(); + case CurToken.GroupCode of + 10: CircleCenterX := CurToken.FloatValue; + 20: CircleCenterY := CurToken.FloatValue; + 30: CircleCenterZ := CurToken.FloatValue; + 40: CircleRadius := CurToken.FloatValue; + end; end; end; -function TvDXFVectorialReader.ReadENTITIES_LINE(AStrings: TStrings; - var AIndex: Integer; AData: TvVectorialDocument): Boolean; +{ +100 Subclass marker (AcDbEllipse) +10 Center point (in WCS) DXF: X value; APP: 3D point +20, 30 DXF: Y and Z values of center point (in WCS) +11 Endpoint of major axis, relative to the center (in WCS) DXF: X value; APP: 3D point +21, 31 DXF: Y and Z values of endpoint of major axis, relative to the center (in WCS) +210 Extrusion direction (optional; default = 0, 0, 1) DXF: X value; APP: 3D vector +220, 230 DXF: Y and Z values of extrusion direction (optional) +40 Ratio of minor axis to major axis +41 Start parameter (this value is 0.0 for a full ellipse) +42 End parameter (this value is 2pi for a full ellipse) +} +procedure TvDXFVectorialReader.ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; + AData: TvVectorialDocument); var - StrSectionNum, StrSectionValue: string; - IntSectionNum: Integer; - FloatSectionValue: double; + CurToken: TDXFToken; + i: Integer; + CenterX, CenterY, CenterZ: Double; begin - Result := False; + for i := 0 to ATokens.Count - 1 do + begin + // Now read and process the item name + CurToken := TDXFToken(ATokens.Items[i]); - // Now read and process the item name - StrSectionNum := AStrings.Strings[AIndex]; - StrSectionValue := AStrings.Strings[AIndex+1]; + // Avoid an exception by previously checking if the conversion can be made + if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or + (CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue; - if (StrSectionValue = 'LINE') or - (StrSectionValue = 'ENDSEC') then - begin - Exit(True); - end - else - begin - Inc(AIndex, 2); - - IntSectionNum := StrToInt(Trim(StrSectionNum)); - FloatSectionValue := StrToFloat(Trim(StrSectionValue)); - - case IntSectionNum of - 10: LineStartX := FloatSectionValue; - 20: LineStartY := FloatSectionValue; - 30: LineStartZ := FloatSectionValue; - 11: LineEndX := FloatSectionValue; - 21: LineEndY := FloatSectionValue; - 31: LineEndZ := FloatSectionValue; + CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue)); + + case CurToken.GroupCode of + 10: CenterX := CurToken.FloatValue; + 20: CenterY := CurToken.FloatValue; + 30: CenterZ := CurToken.FloatValue; end; end; end; -function TvDXFVectorialReader.GetCoordinate(AStr: shortstring): Integer; +{ +100 Subclass marker (AcDbText) +39 Thickness (optional; default = 0) +10 First alignment point (in OCS) DXF: X value; APP: 3D point +20, 30 DXF: Y and Z values of first alignment point (in OCS) +40 Text height +1 Default value (the string itself) +50 Text rotation (optional; default = 0) +41 Relative X scale factor-width (optional; default = 1) + This value is also adjusted when fit-type text is used. +51 Oblique angle (optional; default = 0) +7 Text style name (optional, default = STANDARD) +71 Text generation flags (optional, default = 0): + 2 = Text is backward (mirrored in X). + 4 = Text is upside down (mirrored in Y). +72 Horizontal text justification type (optional, default = 0) integer codes (not bit-coded) + 0 = Left; 1= Center; 2 = Right + 3 = Aligned (if vertical alignment = 0) + 4 = Middle (if vertical alignment = 0) + 5 = Fit (if vertical alignment = 0) + See the Group 72 and 73 integer codes table for clarification. +11 Second alignment point (in OCS) (optional) + DXF: X value; APP: 3D point + This value is meaningful only if the value of a 72 or 73 group is nonzero (if the justification is anything other than baseline/left). +21, 31 DXF: Y and Z values of second alignment point (in OCS) (optional) +210 Extrusion direction (optional; default = 0, 0, 1) + DXF: X value; APP: 3D vector +220, 230 DXF: Y and Z values of extrusion direction (optional) +73 Vertical text justification type (optional, default = 0): integer codes (not bit- coded): + 0 = Baseline; 1 = Bottom; 2 = Middle; 3 = Top + See the Group 72 and 73 integer codes table for clarification. +} +procedure TvDXFVectorialReader.ReadENTITIES_TEXT(ATokens: TDXFTokens; + AData: TvVectorialDocument); begin - Result := INT_COORDINATE_NONE; - if AStr = '' then Exit - else if AStr[1] = 'X' then Result := INT_COORDINATE_X - else if AStr[1] = 'Y' then Result := INT_COORDINATE_Y - else if AStr[1] = 'Z' then Result := INT_COORDINATE_Z; end; function TvDXFVectorialReader.GetCoordinateValue(AStr: shortstring): Double; begin Result := 0.0; - if Length(AStr) <= 1 then Exit; +{ if Length(AStr) <= 1 then Exit; + + Result := StrToFloat(Copy(AStr, 2, Length(AStr) - 1));} +end; + +constructor TvDXFVectorialReader.Create; +begin + inherited Create; - Result := StrToFloat(Copy(AStr, 2, Length(AStr) - 1)); + Tokenizer := TDXFTokenizer.Create; +end; + +destructor TvDXFVectorialReader.Destroy; +begin + Tokenizer.Free; + + inherited Destroy; end; {@@ @@ -286,10 +533,18 @@ procedure TvDXFVectorialReader.ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); var i: Integer; + CurToken, CurTokenFirstChild: TDXFToken; begin - i := 0; - while i < AStrings.Count - 1 do - if ReadSection(AStrings, i, AData) then Break; + Tokenizer.ReadFromStrings(AStrings); + + for i := 0 to Tokenizer.Tokens.Count - 1 do + begin + CurToken := TDXFToken(Tokenizer.Tokens.Items[i]); + CurTokenFirstChild := TDXFToken(CurToken.Childs.Items[0]); + + if CurTokenFirstChild.StrValue = 'ENTITIES' then + ReadENTITIES(CurToken.Childs, AData); + end; end; initialization |
