< Summary

Line coverage
100%
Covered lines: 489
Uncovered lines: 0
Coverable lines: 489
Total lines: 1234
Line coverage: 100%
Branch coverage
100%
Covered branches: 508
Total branches: 508
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
FromStream(...)100%384384100%
ParseTriplet(...)100%3636100%
ParseCurveIndex(...)100%88100%
ParseCurveIndex(...)100%66100%
ParseFreeFormType(...)100%1818100%
ParseSurfaceConnection(...)100%2020100%
ParseGroupName(...)100%44100%
ParseApproximationTechnique(...)100%3232100%

File(s)

https://raw.githubusercontent.com/JeremyAnsel/JeremyAnsel.Media.WavefrontObj/dbbfcd213b7c54f49194f82fcd56e790cca193c3/JeremyAnsel.Media.WavefrontObj/JeremyAnsel.Media.WavefrontObj/ObjFileReader.cs

#LineLine coverage
 1// <copyright file="ObjFileReader.cs" company="Jérémy Ansel">
 2// Copyright (c) 2017, 2019 Jérémy Ansel
 3// </copyright>
 4// <license>
 5// Licensed under the MIT license. See LICENSE.txt
 6// </license>
 7
 8using System.Diagnostics.CodeAnalysis;
 9using System.Globalization;
 10
 11namespace JeremyAnsel.Media.WavefrontObj
 12{
 13    internal static class ObjFileReader
 14    {
 15        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 16        public static ObjFile FromStream(Stream? stream)
 17        {
 81918            if (stream == null)
 19            {
 320                throw new ArgumentNullException(nameof(stream));
 21            }
 22
 81623            var obj = new ObjFile();
 81624            var context = new ObjFileReaderContext(obj);
 81625            var lineReader = new LineReader();
 26
 533727            foreach (var values in lineReader.Read(stream))
 28            {
 211229                switch (values[0].ToLowerInvariant())
 30                {
 31                    case "v":
 32                        {
 50433                            if (values.Length < 4)
 34                            {
 935                                throw new InvalidDataException("A v statement must specify at least 3 values.");
 36                            }
 37
 49538                            float x = float.Parse(values[1], CultureInfo.InvariantCulture);
 49539                            float y = float.Parse(values[2], CultureInfo.InvariantCulture);
 49540                            float z = float.Parse(values[3], CultureInfo.InvariantCulture);
 49541                            float w = 1.0f;
 49542                            bool hasColor = false;
 49543                            float r = 0.0f;
 49544                            float g = 0.0f;
 49545                            float b = 0.0f;
 49546                            float a = 1.0f;
 47
 49548                            if (values.Length == 4 || values.Length == 5)
 49                            {
 48350                                if (values.Length == 5)
 51                                {
 352                                    w = float.Parse(values[4], CultureInfo.InvariantCulture);
 53                                }
 54                            }
 1255                            else if (values.Length == 7 || values.Length == 8)
 56                            {
 657                                hasColor = true;
 658                                r = float.Parse(values[4], CultureInfo.InvariantCulture);
 659                                g = float.Parse(values[5], CultureInfo.InvariantCulture);
 660                                b = float.Parse(values[6], CultureInfo.InvariantCulture);
 61
 662                                if (values.Length == 8)
 63                                {
 364                                    a = float.Parse(values[7], CultureInfo.InvariantCulture);
 65                                }
 66                            }
 67                            else
 68                            {
 669                                throw new InvalidDataException("A v statement has too many values.");
 70                            }
 71
 48972                            var v = new ObjVertex();
 48973                            v.Position = new ObjVector4(x, y, z, w);
 74
 48975                            if (hasColor)
 76                            {
 677                                v.Color = new ObjVector4(r, g, b, a);
 78                            }
 79
 48980                            obj.Vertices.Add(v);
 48981                            break;
 82                        }
 83
 84                    case "vp":
 85                        {
 18086                            if (values.Length < 2)
 87                            {
 388                                throw new InvalidDataException("A vp statement must specify at least 1 value.");
 89                            }
 90
 17791                            var v = new ObjVector3();
 17792                            v.X = float.Parse(values[1], CultureInfo.InvariantCulture);
 93
 17794                            if (values.Length == 2)
 95                            {
 396                                v.Y = 0.0f;
 397                                v.Z = 1.0f;
 98                            }
 17499                            else if (values.Length == 3)
 100                            {
 3101                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 3102                                v.Z = 1.0f;
 103                            }
 171104                            else if (values.Length == 4)
 105                            {
 168106                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 168107                                v.Z = float.Parse(values[3], CultureInfo.InvariantCulture);
 108                            }
 109                            else
 110                            {
 3111                                throw new InvalidDataException("A vp statement has too many values.");
 112                            }
 113
 174114                            obj.ParameterSpaceVertices.Add(v);
 174115                            break;
 116                        }
 117
 118                    case "vn":
 119                        {
 36120                            if (values.Length < 4)
 121                            {
 9122                                throw new InvalidDataException("A vn statement must specify 3 values.");
 123                            }
 124
 27125                            if (values.Length != 4)
 126                            {
 3127                                throw new InvalidDataException("A vn statement has too many values.");
 128                            }
 129
 24130                            var v = new ObjVector3();
 24131                            v.X = float.Parse(values[1], CultureInfo.InvariantCulture);
 24132                            v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 24133                            v.Z = float.Parse(values[3], CultureInfo.InvariantCulture);
 134
 24135                            obj.VertexNormals.Add(v);
 24136                            break;
 137                        }
 138
 139                    case "vt":
 140                        {
 33141                            if (values.Length < 2)
 142                            {
 3143                                throw new InvalidDataException("A vt statement must specify at least 1 value.");
 144                            }
 145
 30146                            var v = new ObjVector3();
 30147                            v.X = float.Parse(values[1], CultureInfo.InvariantCulture);
 148
 30149                            if (values.Length == 2)
 150                            {
 21151                                v.Y = 0.0f;
 21152                                v.Z = 0.0f;
 153                            }
 9154                            else if (values.Length == 3)
 155                            {
 3156                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 3157                                v.Z = 0.0f;
 158                            }
 6159                            else if (values.Length == 4)
 160                            {
 3161                                v.Y = float.Parse(values[2], CultureInfo.InvariantCulture);
 3162                                v.Z = float.Parse(values[3], CultureInfo.InvariantCulture);
 163                            }
 164                            else
 165                            {
 3166                                throw new InvalidDataException("A vt statement has too many values.");
 167                            }
 168
 27169                            obj.TextureVertices.Add(v);
 27170                            break;
 171                        }
 172
 173                    case "cstype":
 42174                        ObjFileReader.ParseFreeFormType(context, values);
 30175                        break;
 176
 177                    case "deg":
 21178                        if (values.Length < 2)
 179                        {
 3180                            throw new InvalidDataException("A deg statement must specify at least 1 value.");
 181                        }
 182
 18183                        if (values.Length == 2)
 184                        {
 3185                            context.DegreeU = int.Parse(values[1], CultureInfo.InvariantCulture);
 3186                            context.DegreeV = 0;
 187                        }
 15188                        else if (values.Length == 3)
 189                        {
 12190                            context.DegreeU = int.Parse(values[1], CultureInfo.InvariantCulture);
 12191                            context.DegreeV = int.Parse(values[2], CultureInfo.InvariantCulture);
 192                        }
 193                        else
 194                        {
 3195                            throw new InvalidDataException("A deg statement has too many values.");
 196                        }
 197
 198                        break;
 199
 200                    case "bmat":
 201                        {
 15202                            if (values.Length < 2)
 203                            {
 3204                                throw new InvalidDataException("A bmat statement must specify a direction.");
 205                            }
 206
 207                            int d;
 208
 12209                            if (string.Equals(values[1], "u", StringComparison.OrdinalIgnoreCase))
 210                            {
 6211                                d = 1;
 212                            }
 6213                            else if (string.Equals(values[1], "v", StringComparison.OrdinalIgnoreCase))
 214                            {
 3215                                d = 2;
 216                            }
 217                            else
 218                            {
 3219                                throw new InvalidDataException("A bmat statement has an unknown direction.");
 220                            }
 221
 9222                            int count = (context.DegreeU + 1) * (context.DegreeV + 1);
 223
 9224                            if (values.Length != count + 2)
 225                            {
 3226                                throw new InvalidDataException("A bmat statement has too many or too few values.");
 227                            }
 228
 6229                            var matrix = new float[count];
 230
 156231                            for (int i = 0; i < count; i++)
 232                            {
 72233                                matrix[i] = float.Parse(values[2 + i], CultureInfo.InvariantCulture);
 234                            }
 235
 236                            switch (d)
 237                            {
 238                                case 1:
 3239                                    context.BasicMatrixU = matrix;
 3240                                    break;
 241
 242                                case 2:
 3243                                    context.BasicMatrixV = matrix;
 3244                                    break;
 245                            }
 246
 247                            break;
 248                        }
 249
 250                    case "step":
 12251                        if (values.Length < 2)
 252                        {
 3253                            throw new InvalidDataException("A step statement must specify at least 1 value.");
 254                        }
 255
 9256                        if (values.Length == 2)
 257                        {
 3258                            context.StepU = float.Parse(values[1], CultureInfo.InvariantCulture);
 3259                            context.StepV = 1.0f;
 260                        }
 6261                        else if (values.Length == 3)
 262                        {
 3263                            context.StepU = float.Parse(values[1], CultureInfo.InvariantCulture);
 3264                            context.StepV = float.Parse(values[2], CultureInfo.InvariantCulture);
 265                        }
 266                        else
 267                        {
 3268                            throw new InvalidDataException("A step statement has too many values.");
 269                        }
 270
 271                        break;
 272
 273                    case "p":
 274                        {
 102275                            if (values.Length < 2)
 276                            {
 3277                                throw new InvalidDataException("A p statement must specify at least 1 value.");
 278                            }
 279
 99280                            var point = new ObjPoint();
 281
 354282                            for (int i = 1; i < values.Length; i++)
 283                            {
 105284                                point.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 285                            }
 286
 72287                            context.ApplyAttributesToElement(point);
 72288                            context.ApplyAttributesToPolygonalElement(point);
 289
 72290                            obj.Points.Add(point);
 291
 288292                            foreach (var group in context.GetCurrentGroups())
 293                            {
 72294                                group.Points.Add(point);
 295                            }
 296
 297                            break;
 298                        }
 299
 300                    case "l":
 301                        {
 12302                            if (values.Length < 3)
 303                            {
 6304                                throw new InvalidDataException("A l statement must specify at least 2 values.");
 305                            }
 306
 6307                            var line = new ObjLine();
 308
 48309                            for (int i = 1; i < values.Length; i++)
 310                            {
 18311                                line.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 312                            }
 313
 6314                            context.ApplyAttributesToElement(line);
 6315                            context.ApplyAttributesToPolygonalElement(line);
 316
 6317                            obj.Lines.Add(line);
 318
 24319                            foreach (var group in context.GetCurrentGroups())
 320                            {
 6321                                group.Lines.Add(line);
 322                            }
 323
 324                            break;
 325                        }
 326
 327                    case "f":
 328                    case "fo":
 329                        {
 30330                            if (values.Length < 4)
 331                            {
 18332                                throw new InvalidDataException("A f statement must specify at least 3 values.");
 333                            }
 334
 12335                            var face = new ObjFace();
 336
 144337                            for (int i = 1; i < values.Length; i++)
 338                            {
 60339                                face.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 340                            }
 341
 12342                            context.ApplyAttributesToElement(face);
 12343                            context.ApplyAttributesToPolygonalElement(face);
 344
 12345                            obj.Faces.Add(face);
 346
 48347                            foreach (var group in context.GetCurrentGroups())
 348                            {
 12349                                group.Faces.Add(face);
 350                            }
 351
 352                            break;
 353                        }
 354
 355                    case "curv":
 356                        {
 135357                            if (values.Length < 5)
 358                            {
 12359                                throw new InvalidDataException("A curv statement must specify at least 4 values.");
 360                            }
 361
 123362                            var curve = new ObjCurve();
 363
 123364                            curve.StartParameter = float.Parse(values[1], CultureInfo.InvariantCulture);
 123365                            curve.EndParameter = float.Parse(values[2], CultureInfo.InvariantCulture);
 366
 714367                            for (int i = 3; i < values.Length; i++)
 368                            {
 243369                                int v = int.Parse(values[i], CultureInfo.InvariantCulture);
 370
 243371                                if (v == 0)
 372                                {
 3373                                    throw new InvalidDataException("A curv statement contains an invalid vertex index.")
 374                                }
 375
 240376                                if (v < 0)
 377                                {
 3378                                    v = obj.Vertices.Count + v + 1;
 379                                }
 380
 240381                                if (v <= 0 || v > obj.Vertices.Count)
 382                                {
 6383                                    throw new IndexOutOfRangeException();
 384                                }
 385
 234386                                curve.Vertices.Add(v);
 387                            }
 388
 114389                            context.ApplyAttributesToElement(curve);
 114390                            context.ApplyAttributesToFreeFormElement(curve);
 114391                            context.CurrentFreeFormElement = curve;
 392
 114393                            obj.Curves.Add(curve);
 394
 456395                            foreach (var group in context.GetCurrentGroups())
 396                            {
 114397                                group.Curves.Add(curve);
 398                            }
 399
 400                            break;
 401                        }
 402
 403                    case "curv2":
 404                        {
 183405                            if (values.Length < 3)
 406                            {
 6407                                throw new InvalidDataException("A curv2 statement must specify at least 2 values.");
 408                            }
 409
 177410                            var curve = new ObjCurve2D();
 411
 1038412                            for (int i = 1; i < values.Length; i++)
 413                            {
 351414                                int vp = int.Parse(values[i], CultureInfo.InvariantCulture);
 415
 351416                                if (vp == 0)
 417                                {
 3418                                    throw new InvalidDataException("A curv2 statement contains an invalid parameter spac
 419                                }
 420
 348421                                if (vp < 0)
 422                                {
 3423                                    vp = obj.ParameterSpaceVertices.Count + vp + 1;
 424                                }
 425
 348426                                if (vp <= 0 || vp > obj.ParameterSpaceVertices.Count)
 427                                {
 6428                                    throw new IndexOutOfRangeException();
 429                                }
 430
 342431                                curve.ParameterSpaceVertices.Add(vp);
 432                            }
 433
 168434                            context.ApplyAttributesToElement(curve);
 168435                            context.ApplyAttributesToFreeFormElement(curve);
 168436                            context.CurrentFreeFormElement = curve;
 437
 168438                            obj.Curves2D.Add(curve);
 439
 672440                            foreach (var group in context.GetCurrentGroups())
 441                            {
 168442                                group.Curves2D.Add(curve);
 443                            }
 444
 445                            break;
 446                        }
 447
 448                    case "surf":
 449                        {
 78450                            if (values.Length < 6)
 451                            {
 18452                                throw new InvalidDataException("A surf statement must specify at least 5 values.");
 453                            }
 454
 60455                            var surface = new ObjSurface();
 456
 60457                            surface.StartParameterU = float.Parse(values[1], CultureInfo.InvariantCulture);
 60458                            surface.EndParameterU = float.Parse(values[2], CultureInfo.InvariantCulture);
 60459                            surface.StartParameterV = float.Parse(values[3], CultureInfo.InvariantCulture);
 60460                            surface.EndParameterV = float.Parse(values[4], CultureInfo.InvariantCulture);
 461
 252462                            for (int i = 5; i < values.Length; i++)
 463                            {
 72464                                surface.Vertices.Add(ObjFileReader.ParseTriplet(obj, values[i]));
 465                            }
 466
 54467                            context.ApplyAttributesToElement(surface);
 54468                            context.ApplyAttributesToFreeFormElement(surface);
 54469                            context.CurrentFreeFormElement = surface;
 470
 54471                            obj.Surfaces.Add(surface);
 472
 216473                            foreach (var group in context.GetCurrentGroups())
 474                            {
 54475                                group.Surfaces.Add(surface);
 476                            }
 477
 478                            break;
 479                        }
 480
 481                    case "parm":
 21482                        if (context.CurrentFreeFormElement == null)
 483                        {
 484                            break;
 485                        }
 486
 18487                        if (values.Length < 4)
 488                        {
 9489                            throw new InvalidDataException("A parm statement must specify at least 3 values.");
 490                        }
 491
 492                        List<float> parameters;
 493
 9494                        if (string.Equals(values[1], "u", StringComparison.OrdinalIgnoreCase))
 495                        {
 3496                            parameters = context.CurrentFreeFormElement.ParametersU;
 497                        }
 6498                        else if (string.Equals(values[1], "v", StringComparison.OrdinalIgnoreCase))
 499                        {
 3500                            parameters = context.CurrentFreeFormElement.ParametersV;
 501                        }
 502                        else
 503                        {
 3504                            throw new InvalidDataException("A parm statement has an unknown direction.");
 505                        }
 506
 48507                        for (int i = 2; i < values.Length; i++)
 508                        {
 18509                            parameters.Add(float.Parse(values[i], CultureInfo.InvariantCulture));
 510                        }
 511
 6512                        break;
 513
 514                    case "trim":
 30515                        if (context.CurrentFreeFormElement == null)
 516                        {
 517                            break;
 518                        }
 519
 27520                        ObjFileReader.ParseCurveIndex(context.CurrentFreeFormElement.OuterTrimmingCurves, obj, values);
 3521                        break;
 522
 523                    case "hole":
 30524                        if (context.CurrentFreeFormElement == null)
 525                        {
 526                            break;
 527                        }
 528
 27529                        ObjFileReader.ParseCurveIndex(context.CurrentFreeFormElement.InnerTrimmingCurves, obj, values);
 3530                        break;
 531
 532                    case "scrv":
 30533                        if (context.CurrentFreeFormElement == null)
 534                        {
 535                            break;
 536                        }
 537
 27538                        ObjFileReader.ParseCurveIndex(context.CurrentFreeFormElement.SequenceCurves, obj, values);
 3539                        break;
 540
 541                    case "sp":
 18542                        if (context.CurrentFreeFormElement == null)
 543                        {
 544                            break;
 545                        }
 546
 15547                        if (values.Length < 2)
 548                        {
 3549                            throw new InvalidDataException("A sp statement must specify at least 1 value.");
 550                        }
 551
 36552                        for (int i = 1; i < values.Length; i++)
 553                        {
 15554                            int vp = int.Parse(values[i], CultureInfo.InvariantCulture);
 555
 15556                            if (vp == 0)
 557                            {
 3558                                throw new InvalidDataException("A sp statement contains an invalid parameter space verte
 559                            }
 560
 12561                            if (vp < 0)
 562                            {
 3563                                vp = obj.ParameterSpaceVertices.Count + vp + 1;
 564                            }
 565
 12566                            if (vp <= 0 || vp > obj.ParameterSpaceVertices.Count)
 567                            {
 6568                                throw new IndexOutOfRangeException();
 569                            }
 570
 6571                            context.CurrentFreeFormElement.SpecialPoints.Add(vp);
 572                        }
 573
 3574                        break;
 575
 576                    case "end":
 18577                        context.CurrentFreeFormElement = null;
 18578                        break;
 579
 580                    case "con":
 66581                        ObjFileReader.ParseSurfaceConnection(obj, values);
 3582                        break;
 583
 584                    case "g":
 66585                        ObjFileReader.ParseGroupName(values, context);
 66586                        break;
 587
 588                    case "s":
 27589                        if (values.Length < 2)
 590                        {
 3591                            throw new InvalidDataException("A s statement must specify a value.");
 592                        }
 593
 24594                        if (values.Length != 2)
 595                        {
 3596                            throw new InvalidDataException("A s statement has too many values.");
 597                        }
 598
 21599                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 600                        {
 3601                            context.SmoothingGroupNumber = 0;
 602                        }
 603                        else
 604                        {
 18605                            context.SmoothingGroupNumber = long.Parse(values[1], CultureInfo.InvariantCulture);
 606                        }
 607
 18608                        break;
 609
 610                    case "mg":
 30611                        if (values.Length < 2)
 612                        {
 3613                            throw new InvalidDataException("A mg statement must specify a value.");
 614                        }
 615
 27616                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 617                        {
 3618                            context.MergingGroupNumber = 0;
 619                        }
 620                        else
 621                        {
 24622                            context.MergingGroupNumber = int.Parse(values[1], CultureInfo.InvariantCulture);
 623                        }
 624
 27625                        if (context.MergingGroupNumber == 0)
 626                        {
 9627                            if (values.Length > 3)
 628                            {
 3629                                throw new InvalidDataException("A mg statement has too many values.");
 630                            }
 631                        }
 632                        else
 633                        {
 18634                            if (values.Length != 3)
 635                            {
 6636                                throw new InvalidDataException("A mg statement has too many or too few values.");
 637                            }
 638
 12639                            float res = float.Parse(values[2], CultureInfo.InvariantCulture);
 640
 12641                            obj.MergingGroupResolutions[context.MergingGroupNumber] = res;
 642                        }
 643
 12644                        break;
 645
 646                    case "o":
 30647                        if (values.Length == 1)
 648                        {
 3649                            context.ObjectName = null;
 3650                            break;
 651                        }
 652
 27653                        if (values.Length != 2)
 654                        {
 3655                            throw new InvalidDataException("A o statement has too many values.");
 656                        }
 657
 24658                        context.ObjectName = values[1];
 24659                        break;
 660
 661                    case "bevel":
 27662                        if (values.Length < 2)
 663                        {
 3664                            throw new InvalidDataException("A bevel statement must specify a name.");
 665                        }
 666
 24667                        if (values.Length != 2)
 668                        {
 3669                            throw new InvalidDataException("A bevel statement has too many values.");
 670                        }
 671
 21672                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 673                        {
 15674                            context.IsBevelInterpolationEnabled = true;
 675                        }
 6676                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 677                        {
 3678                            context.IsBevelInterpolationEnabled = false;
 679                        }
 680                        else
 681                        {
 3682                            throw new InvalidDataException("A bevel statement must specify on or off.");
 683                        }
 684
 685                        break;
 686
 687                    case "c_interp":
 27688                        if (values.Length < 2)
 689                        {
 3690                            throw new InvalidDataException("A c_interp statement must specify a name.");
 691                        }
 692
 24693                        if (values.Length != 2)
 694                        {
 3695                            throw new InvalidDataException("A c_interp statement has too many values.");
 696                        }
 697
 21698                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 699                        {
 15700                            context.IsColorInterpolationEnabled = true;
 701                        }
 6702                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 703                        {
 3704                            context.IsColorInterpolationEnabled = false;
 705                        }
 706                        else
 707                        {
 3708                            throw new InvalidDataException("A c_interp statement must specify on or off.");
 709                        }
 710
 711                        break;
 712
 713                    case "d_interp":
 27714                        if (values.Length < 2)
 715                        {
 3716                            throw new InvalidDataException("A d_interp statement must specify a name.");
 717                        }
 718
 24719                        if (values.Length != 2)
 720                        {
 3721                            throw new InvalidDataException("A d_interp statement has too many values.");
 722                        }
 723
 21724                        if (string.Equals(values[1], "on", StringComparison.OrdinalIgnoreCase))
 725                        {
 15726                            context.IsDissolveInterpolationEnabled = true;
 727                        }
 6728                        else if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 729                        {
 3730                            context.IsDissolveInterpolationEnabled = false;
 731                        }
 732                        else
 733                        {
 3734                            throw new InvalidDataException("A d_interp statement must specify on or off.");
 735                        }
 736
 737                        break;
 738
 739                    case "lod":
 30740                        if (values.Length < 2)
 741                        {
 3742                            throw new InvalidDataException("A lod statement must specify a value.");
 743                        }
 744
 27745                        if (values.Length != 2)
 746                        {
 3747                            throw new InvalidDataException("A lod statement has too many values.");
 748                        }
 749
 24750                        context.LevelOfDetail = int.Parse(values[1], CultureInfo.InvariantCulture);
 24751                        break;
 752
 753                    case "maplib":
 9754                        if (values.Length < 2)
 755                        {
 3756                            throw new InvalidDataException("A maplib statement must specify a file name.");
 757                        }
 758
 24759                        for (int i = 1; i < values.Length; i++)
 760                        {
 9761                            if (!Path.HasExtension(values[i]))
 762                            {
 3763                                throw new InvalidDataException("A file name must have an extension.");
 764                            }
 765
 6766                            obj.MapLibraries.Add(values[i]);
 767                        }
 768
 3769                        break;
 770
 771                    case "mtllib":
 9772                        if (values.Length < 2)
 773                        {
 3774                            throw new InvalidDataException("A mtllib statement must specify a file name.");
 775                        }
 776
 777                        {
 6778                            string name = string.Join(" ", values, 1, values.Length - 1);
 779
 6780                            if (!Path.HasExtension(name))
 781                            {
 3782                                throw new InvalidDataException("A file name must have an extension.");
 783                            }
 784
 3785                            obj.MaterialLibraries.Add(name);
 786                        }
 787
 3788                        break;
 789
 790                    case "usemap":
 33791                        if (values.Length < 2)
 792                        {
 3793                            throw new InvalidDataException("A usemap statement must specify a value.");
 794                        }
 795
 30796                        if (values.Length != 2)
 797                        {
 3798                            throw new InvalidDataException("A usemap statement has too many values.");
 799                        }
 800
 27801                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 802                        {
 3803                            context.MapName = null;
 804                        }
 805                        else
 806                        {
 24807                            context.MapName = values[1];
 808                        }
 809
 24810                        break;
 811
 812                    case "usemtl":
 33813                        if (values.Length < 2)
 814                        {
 3815                            throw new InvalidDataException("A usemtl statement must specify a value.");
 816                        }
 817
 30818                        if (string.Equals(values[1], "off", StringComparison.OrdinalIgnoreCase))
 819                        {
 6820                            if (values.Length != 2)
 821                            {
 3822                                throw new InvalidDataException("A usemtl statement has too many values.");
 823                            }
 824
 3825                            context.MaterialName = null;
 826                        }
 827                        else
 828                        {
 24829                            context.MaterialName = string.Join(" ", values, 1, values.Length - 1);
 830                        }
 831
 24832                        break;
 833
 834                    case "shadow_obj":
 18835                        if (values.Length < 2)
 836                        {
 3837                            throw new InvalidDataException("A shadow_obj statement must specify a file name.");
 838                        }
 839
 15840                        if (values.Length != 2)
 841                        {
 3842                            throw new InvalidDataException("A shadow_obj statement has too many values.");
 843                        }
 844
 12845                        if (!Path.HasExtension(values[1]))
 846                        {
 3847                            throw new InvalidDataException("A file name must have an extension.");
 848                        }
 849
 9850                        obj.ShadowObjectFileName = values[1];
 9851                        break;
 852
 853                    case "trace_obj":
 12854                        if (values.Length < 2)
 855                        {
 3856                            throw new InvalidDataException("A trace_obj statement must specify a file name.");
 857                        }
 858
 9859                        if (values.Length != 2)
 860                        {
 3861                            throw new InvalidDataException("A trace_obj statement has too many values.");
 862                        }
 863
 6864                        if (!Path.HasExtension(values[1]))
 865                        {
 3866                            throw new InvalidDataException("A file name must have an extension.");
 867                        }
 868
 3869                        obj.TraceObjectFileName = values[1];
 3870                        break;
 871
 872                    case "ctech":
 54873                        context.CurveApproximationTechnique = ObjFileReader.ParseApproximationTechnique(values);
 27874                        break;
 875
 876                    case "stech":
 66877                        context.SurfaceApproximationTechnique = ObjFileReader.ParseApproximationTechnique(values);
 30878                        break;
 879
 880                    case "bsp":
 881                    case "bzp":
 882                    case "cdc":
 883                    case "cdp":
 884                    case "res":
 15885                        throw new NotImplementedException(string.Concat(values[0], " statement have been replaced by fre
 886                }
 887            }
 888
 297889            obj.HeaderText = string.Join("\n", lineReader.HeaderTextLines.ToArray());
 890
 297891            return obj;
 892        }
 893
 894        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 895        private static ObjTriplet ParseTriplet(ObjFile obj, string value)
 896        {
 255897            var values = value.Split('/');
 898
 255899            if (values.Length > 3)
 900            {
 3901                throw new InvalidDataException("A triplet has too many values.");
 902            }
 903
 252904            int v = !string.IsNullOrEmpty(values[0]) ? int.Parse(values[0], CultureInfo.InvariantCulture) : 0;
 905
 252906            if (v == 0)
 907            {
 6908                throw new InvalidDataException("A triplet must specify a vertex index.");
 909            }
 910
 246911            if (v < 0)
 912            {
 6913                v = obj.Vertices.Count + v + 1;
 914            }
 915
 246916            if (v <= 0 || v > obj.Vertices.Count)
 917            {
 12918                throw new IndexOutOfRangeException();
 919            }
 920
 234921            int vt = values.Length > 1 && !string.IsNullOrEmpty(values[1]) ? int.Parse(values[1], CultureInfo.InvariantC
 922
 234923            if (vt != 0)
 924            {
 12925                if (vt < 0)
 926                {
 3927                    vt = obj.TextureVertices.Count + vt + 1;
 928                }
 929
 12930                if (vt <= 0 || vt > obj.TextureVertices.Count)
 931                {
 6932                    throw new IndexOutOfRangeException();
 933                }
 934            }
 935
 228936            int vn = values.Length > 2 && !string.IsNullOrEmpty(values[2]) ? int.Parse(values[2], CultureInfo.InvariantC
 937
 228938            if (vn != 0)
 939            {
 12940                if (vn < 0)
 941                {
 3942                    vn = obj.VertexNormals.Count + vn + 1;
 943                }
 944
 12945                if (vn <= 0 || vn > obj.VertexNormals.Count)
 946                {
 6947                    throw new IndexOutOfRangeException();
 948                }
 949            }
 950
 222951            return new ObjTriplet(v, vt, vn);
 952        }
 953
 954        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 955        private static ObjCurveIndex ParseCurveIndex(ObjFile obj, string[] values, int index)
 956        {
 87957            float start = float.Parse(values[index], CultureInfo.InvariantCulture);
 87958            float end = float.Parse(values[index + 1], CultureInfo.InvariantCulture);
 87959            int curve2D = int.Parse(values[index + 2], CultureInfo.InvariantCulture);
 960
 87961            if (curve2D == 0)
 962            {
 15963                throw new InvalidDataException("A curve index must specify an index.");
 964            }
 965
 72966            if (curve2D < 0)
 967            {
 15968                curve2D = obj.Curves2D.Count + curve2D + 1;
 969            }
 970
 72971            if (curve2D <= 0 || curve2D > obj.Curves2D.Count)
 972            {
 30973                throw new IndexOutOfRangeException();
 974            }
 975
 42976            return new ObjCurveIndex(start, end, curve2D);
 977        }
 978
 979        private static void ParseCurveIndex(List<ObjCurveIndex> curves, ObjFile obj, string[] values)
 980        {
 81981            if (values.Length < 4)
 982            {
 27983                throw new InvalidDataException(string.Concat("A ", values[0], " statement must specify at least 3 value.
 984            }
 985
 54986            if ((values.Length - 1) % 3 != 0)
 987            {
 18988                throw new InvalidDataException(string.Concat("A ", values[0], " statement has too many values."));
 989            }
 990
 108991            for (int i = 1; i < values.Length; i += 3)
 992            {
 45993                curves.Add(ObjFileReader.ParseCurveIndex(obj, values, i));
 994            }
 9995        }
 996
 997        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 998        private static void ParseFreeFormType(ObjFileReaderContext context, string[] values)
 999        {
 421000            if (values.Length < 2)
 1001            {
 31002                throw new InvalidDataException("A cstype statement must specify a value.");
 1003            }
 1004
 1005            string type;
 1006
 391007            if (values.Length == 2)
 1008            {
 181009                context.IsRationalForm = false;
 181010                type = values[1];
 1011            }
 211012            else if (values.Length == 3 && string.Equals(values[1], "rat", StringComparison.OrdinalIgnoreCase))
 1013            {
 181014                context.IsRationalForm = true;
 181015                type = values[2];
 1016            }
 1017            else
 1018            {
 31019                throw new InvalidDataException("A cstype statement has too many values.");
 1020            }
 1021
 361022            switch (type.ToLowerInvariant())
 1023            {
 1024                case "bmatrix":
 61025                    context.FreeFormType = ObjFreeFormType.BasisMatrix;
 61026                    break;
 1027
 1028                case "bezier":
 61029                    context.FreeFormType = ObjFreeFormType.Bezier;
 61030                    break;
 1031
 1032                case "bspline":
 61033                    context.FreeFormType = ObjFreeFormType.BSpline;
 61034                    break;
 1035
 1036                case "cardinal":
 61037                    context.FreeFormType = ObjFreeFormType.Cardinal;
 61038                    break;
 1039
 1040                case "taylor":
 61041                    context.FreeFormType = ObjFreeFormType.Taylor;
 61042                    break;
 1043
 1044                default:
 61045                    throw new InvalidDataException("A cstype statement has an unknown type.");
 1046            }
 1047        }
 1048
 1049        [SuppressMessage("Globalization", "CA1303:Ne pas passer de littéraux en paramètres localisés", Justification = "
 1050        private static void ParseSurfaceConnection(ObjFile obj, string[] values)
 1051        {
 661052            if (values.Length < 9)
 1053            {
 241054                throw new InvalidDataException("A con statement must specify 8 values.");
 1055            }
 1056
 421057            if (values.Length != 9)
 1058            {
 31059                throw new InvalidDataException("A con statement has too many values.");
 1060            }
 1061
 391062            int surface1 = int.Parse(values[1], CultureInfo.InvariantCulture);
 1063
 391064            if (surface1 == 0)
 1065            {
 31066                throw new InvalidDataException("A con statement must specify a surface index.");
 1067            }
 1068
 361069            if (surface1 < 0)
 1070            {
 31071                surface1 = obj.Surfaces.Count + surface1 + 1;
 1072            }
 1073
 361074            if (surface1 <= 0 || surface1 > obj.Surfaces.Count)
 1075            {
 61076                throw new IndexOutOfRangeException();
 1077            }
 1078
 301079            var curve1 = ObjFileReader.ParseCurveIndex(obj, values, 2);
 1080
 211081            int surface2 = int.Parse(values[5], CultureInfo.InvariantCulture);
 1082
 211083            if (surface2 == 0)
 1084            {
 31085                throw new InvalidDataException("A con statement must specify a surface index.");
 1086            }
 1087
 181088            if (surface2 < 0)
 1089            {
 31090                surface2 = obj.Surfaces.Count + surface2 + 1;
 1091            }
 1092
 181093            if (surface2 <= 0 || surface2 > obj.Surfaces.Count)
 1094            {
 61095                throw new IndexOutOfRangeException();
 1096            }
 1097
 121098            var curve2 = ObjFileReader.ParseCurveIndex(obj, values, 6);
 1099
 31100            var connection = new ObjSurfaceConnection
 31101            {
 31102                Surface1 = surface1,
 31103                Curve2D1 = curve1,
 31104                Surface2 = surface2,
 31105                Curve2D2 = curve2
 31106            };
 1107
 31108            obj.SurfaceConnections.Add(connection);
 31109        }
 1110
 1111        private static void ParseGroupName(string[] values, ObjFileReaderContext context)
 1112        {
 661113            context.GroupNames.Clear();
 1114
 2821115            for (int i = 1; i < values.Length; i++)
 1116            {
 751117                var name = values[i];
 1118
 751119                if (!string.Equals(name, "default", StringComparison.OrdinalIgnoreCase))
 1120                {
 511121                    context.GroupNames.Add(name);
 1122                }
 1123            }
 1124
 661125            context.GetCurrentGroups();
 661126        }
 1127
 1128        private static ObjApproximationTechnique ParseApproximationTechnique(string[] values)
 1129        {
 1130            ObjApproximationTechnique technique;
 1131
 1201132            if (values.Length < 2)
 1133            {
 61134                throw new InvalidDataException(string.Concat("A ", values[0], " statement must specify a technique."));
 1135            }
 1136
 1141137            switch (values[1].ToLowerInvariant())
 1138            {
 1139                case "cparm":
 1140                    {
 91141                        if (values.Length < 3)
 1142                        {
 31143                            throw new InvalidDataException(string.Concat("A ", values[0], " cparm statement must specify
 1144                        }
 1145
 61146                        if (values.Length != 3)
 1147                        {
 31148                            throw new InvalidDataException(string.Concat("A ", values[0], " cparm statement has too many
 1149                        }
 1150
 31151                        float res = float.Parse(values[2], CultureInfo.InvariantCulture);
 31152                        technique = new ObjConstantParametricSubdivisionTechnique(res);
 31153                        break;
 1154                    }
 1155
 1156                case "cparma":
 1157                    {
 121158                        if (values.Length < 4)
 1159                        {
 61160                            throw new InvalidDataException(string.Concat("A ", values[0], " cparma statement must specif
 1161                        }
 1162
 61163                        if (values.Length != 4)
 1164                        {
 31165                            throw new InvalidDataException(string.Concat("A ", values[0], " cparma statement has too man
 1166                        }
 1167
 31168                        float resU = float.Parse(values[2], CultureInfo.InvariantCulture);
 31169                        float resV = float.Parse(values[3], CultureInfo.InvariantCulture);
 31170                        technique = new ObjConstantParametricSubdivisionTechnique(resU, resV);
 31171                        break;
 1172                    }
 1173
 1174                case "cparmb":
 1175                    {
 91176                        if (values.Length < 3)
 1177                        {
 31178                            throw new InvalidDataException(string.Concat("A ", values[0], " cparmb statement must specif
 1179                        }
 1180
 61181                        if (values.Length != 3)
 1182                        {
 31183                            throw new InvalidDataException(string.Concat("A ", values[0], " cparmb statement has too man
 1184                        }
 1185
 31186                        float resU = float.Parse(values[2], CultureInfo.InvariantCulture);
 31187                        technique = new ObjConstantParametricSubdivisionTechnique(resU);
 31188                        break;
 1189                    }
 1190
 1191                case "cspace":
 1192                    {
 541193                        if (values.Length < 3)
 1194                        {
 61195                            throw new InvalidDataException(string.Concat("A ", values[0], " cspace statement must specif
 1196                        }
 1197
 481198                        if (values.Length != 3)
 1199                        {
 61200                            throw new InvalidDataException(string.Concat("A ", values[0], " cspace statement has too man
 1201                        }
 1202
 421203                        float length = float.Parse(values[2], CultureInfo.InvariantCulture);
 421204                        technique = new ObjConstantSpatialSubdivisionTechnique(length);
 421205                        break;
 1206                    }
 1207
 1208                case "curv":
 1209                    {
 241210                        if (values.Length < 4)
 1211                        {
 121212                            throw new InvalidDataException(string.Concat("A ", values[0], " curv statement must specify 
 1213                        }
 1214
 121215                        if (values.Length != 4)
 1216                        {
 61217                            throw new InvalidDataException(string.Concat("A ", values[0], " curv statement has too many 
 1218                        }
 1219
 61220                        float distance = float.Parse(values[2], CultureInfo.InvariantCulture);
 61221                        float angle = float.Parse(values[3], CultureInfo.InvariantCulture);
 61222                        technique = new ObjCurvatureDependentSubdivisionTechnique(distance, angle);
 61223                        break;
 1224                    }
 1225
 1226                default:
 61227                    throw new InvalidDataException(string.Concat("A ", values[0], " statement contains an unknown techni
 1228            }
 1229
 571230            return technique;
 1231        }
 1232    }
 1233}
 1234