From 7bb16b5ae78a8c57954b1ee2160a1346915f6524 Mon Sep 17 00:00:00 2001 From: Levi Purdy Date: Sun, 9 Nov 2025 22:07:12 -0700 Subject: [PATCH 1/6] Add induction demo playback support to Flask GUI --- .devcontainer/Dockerfile | 2 +- .../assets/dxf/induction_demo/air_regions.dxf | 3352 +++++++++++++++ docs/assets/dxf/induction_demo/domain.dxf | 3080 ++++++++++++++ docs/assets/dxf/induction_demo/materials.dxf | 3760 +++++++++++++++++ docs/assets/dxf/induction_demo/rotor_bars.dxf | 3260 ++++++++++++++ docs/assets/dxf/induction_demo/rotor_core.dxf | 3120 ++++++++++++++ .../assets/dxf/induction_demo/stator_only.dxf | 3160 ++++++++++++++ docs/developer-guide/implementation/AGENTS.md | 14 + .../implementation/flask-gui-progress.md | 36 +- .../gui-workflows/induction-demo.md | 99 + docs/user-guide/gui_flask.md | 108 +- inputs/induction_gui_demo.json | 1389 ++++++ mkdocs.yml | 1 + python/gui/AGENTS.md | 23 + python/gui/app_flask.py | 1140 ++++- python/gui/dxf_utils.py | 267 ++ python/gui/render.py | 142 + python/gui/templates/base.html | 149 +- python/gui/templates/index.html | 848 +++- python/visualize_scenario_field.py | 14 +- scripts/maintain_gui_env.sh | 39 + scripts/setup_gui_env.sh | 30 + tests/test_gui_flask.py | 576 ++- 23 files changed, 24482 insertions(+), 127 deletions(-) create mode 100644 docs/assets/dxf/induction_demo/air_regions.dxf create mode 100644 docs/assets/dxf/induction_demo/domain.dxf create mode 100644 docs/assets/dxf/induction_demo/materials.dxf create mode 100644 docs/assets/dxf/induction_demo/rotor_bars.dxf create mode 100644 docs/assets/dxf/induction_demo/rotor_core.dxf create mode 100644 docs/assets/dxf/induction_demo/stator_only.dxf create mode 100644 docs/developer-guide/implementation/AGENTS.md create mode 100644 docs/user-guide/gui-workflows/induction-demo.md create mode 100644 inputs/induction_gui_demo.json create mode 100644 python/gui/dxf_utils.py create mode 100644 python/gui/render.py create mode 100755 scripts/maintain_gui_env.sh create mode 100755 scripts/setup_gui_env.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9e90981..4718038 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -5,4 +5,4 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* # Flask prototype dependencies plus plotting/dxf helpers reused by the GUI -RUN pip3 install --no-cache-dir flask flask-cors numpy matplotlib ezdxf +RUN pip3 install --no-cache-dir flask flask-cors numpy matplotlib ezdxf pytest diff --git a/docs/assets/dxf/induction_demo/air_regions.dxf b/docs/assets/dxf/induction_demo/air_regions.dxf new file mode 100644 index 0000000..9f476d2 --- /dev/null +++ b/docs/assets/dxf/induction_demo/air_regions.dxf @@ -0,0 +1,3352 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460989.8777777776 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460989.8777777776 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +39 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{D5386D6A-308F-49E9-8EE8-9B02EF3401A2} + 9 +$VERSIONGUID + 2 +{2C2AC8A7-E551-4936-983C-FA9EFAB7F728} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +36 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +37 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +LWPOLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0395 + 20 +-0.0063 + 10 +0.0494 + 20 +-0.0078 + 10 +0.0494 + 20 +0.0078 + 10 +0.0395 + 20 +0.0063 + 10 +0.0395 + 20 +-0.0063 + 0 +LWPOLYLINE + 5 +30 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0252 + 20 +0.0311 + 10 +0.0315 + 20 +0.0389 + 10 +0.0179 + 20 +0.0467 + 10 +0.0143 + 20 +0.0373 + 10 +0.0252 + 20 +0.0311 + 0 +LWPOLYLINE + 5 +31 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0143 + 20 +0.0373 + 10 +-0.0179 + 20 +0.0467 + 10 +-0.0315 + 20 +0.0389 + 10 +-0.0252 + 20 +0.0311 + 10 +-0.0143 + 20 +0.0373 + 0 +LWPOLYLINE + 5 +32 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0395 + 20 +0.0063 + 10 +-0.0494 + 20 +0.0078 + 10 +-0.0494 + 20 +-0.0078 + 10 +-0.0395 + 20 +-0.0063 + 10 +-0.0395 + 20 +0.0063 + 0 +LWPOLYLINE + 5 +33 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0252 + 20 +-0.0311 + 10 +-0.0315 + 20 +-0.0389 + 10 +-0.0179 + 20 +-0.0467 + 10 +-0.0143 + 20 +-0.0373 + 10 +-0.0252 + 20 +-0.0311 + 0 +LWPOLYLINE + 5 +34 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0143 + 20 +-0.0373 + 10 +0.0179 + 20 +-0.0467 + 10 +0.0315 + 20 +-0.0389 + 10 +0.0252 + 20 +-0.0311 + 10 +0.0143 + 20 +-0.0373 + 0 +LWPOLYLINE + 5 +35 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +19 + 70 +0 + 10 +0.04 + 20 +0.0 + 10 +0.0376 + 20 +0.0137 + 10 +0.0306 + 20 +0.0257 + 10 +0.02 + 20 +0.0346 + 10 +0.0069 + 20 +0.0394 + 10 +-0.0069 + 20 +0.0394 + 10 +-0.02 + 20 +0.0346 + 10 +-0.0306 + 20 +0.0257 + 10 +-0.0376 + 20 +0.0137 + 10 +-0.04 + 20 +0.0 + 10 +-0.0376 + 20 +-0.0137 + 10 +-0.0306 + 20 +-0.0257 + 10 +-0.02 + 20 +-0.0346 + 10 +-0.0069 + 20 +-0.0394 + 10 +0.0069 + 20 +-0.0394 + 10 +0.02 + 20 +-0.0346 + 10 +0.0306 + 20 +-0.0257 + 10 +0.0376 + 20 +-0.0137 + 10 +0.04 + 20 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +38 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.700723+00:00 + 0 +DICTIONARYVAR + 5 +38 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.703583+00:00 + 0 +ENDSEC + 0 +EOF diff --git a/docs/assets/dxf/induction_demo/domain.dxf b/docs/assets/dxf/induction_demo/domain.dxf new file mode 100644 index 0000000..39c1c68 --- /dev/null +++ b/docs/assets/dxf/induction_demo/domain.dxf @@ -0,0 +1,3080 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460989.8771064817 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460989.8771064817 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +33 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{EE07616D-5BD4-4D4B-AADE-A31A1D41A2C7} + 9 +$VERSIONGUID + 2 +{A0185DAF-64AC-4B9D-AC8A-0550564D253F} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +30 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +31 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +LWPOLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +domain +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.07 + 20 +-0.07 + 10 +0.07 + 20 +-0.07 + 10 +0.07 + 20 +0.07 + 10 +-0.07 + 20 +0.07 + 10 +-0.07 + 20 +-0.07 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +32 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:03:02.317097+00:00 + 0 +DICTIONARYVAR + 5 +32 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:03:02.322017+00:00 + 0 +ENDSEC + 0 +EOF diff --git a/docs/assets/dxf/induction_demo/materials.dxf b/docs/assets/dxf/induction_demo/materials.dxf new file mode 100644 index 0000000..a806d16 --- /dev/null +++ b/docs/assets/dxf/induction_demo/materials.dxf @@ -0,0 +1,3760 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460989.8771064817 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460989.8771064817 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +41 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{49CCA8DC-8041-40D0-8768-EEC63E14F008} + 9 +$VERSIONGUID + 2 +{6D2D62DC-FFFE-454A-A9F2-06900A0D89B9} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +3E +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +3F +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +LWPOLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +material_stator_steel +100 +AcDbPolyline + 90 +25 + 70 +0 + 10 +0.055 + 20 +0.0 + 10 +0.0531 + 20 +0.0142 + 10 +0.0476 + 20 +0.0275 + 10 +0.0389 + 20 +0.0389 + 10 +0.0275 + 20 +0.0476 + 10 +0.0142 + 20 +0.0531 + 10 +0.0 + 20 +0.055 + 10 +-0.0142 + 20 +0.0531 + 10 +-0.0275 + 20 +0.0476 + 10 +-0.0389 + 20 +0.0389 + 10 +-0.0476 + 20 +0.0275 + 10 +-0.0531 + 20 +0.0142 + 10 +-0.055 + 20 +0.0 + 10 +-0.0531 + 20 +-0.0142 + 10 +-0.0476 + 20 +-0.0275 + 10 +-0.0389 + 20 +-0.0389 + 10 +-0.0275 + 20 +-0.0476 + 10 +-0.0142 + 20 +-0.0531 + 10 +0.0 + 20 +-0.055 + 10 +0.0142 + 20 +-0.0531 + 10 +0.0275 + 20 +-0.0476 + 10 +0.0389 + 20 +-0.0389 + 10 +0.0476 + 20 +-0.0275 + 10 +0.0531 + 20 +-0.0142 + 10 +0.055 + 20 +0.0 + 0 +LWPOLYLINE + 5 +30 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0395 + 20 +-0.0063 + 10 +0.0494 + 20 +-0.0078 + 10 +0.0494 + 20 +0.0078 + 10 +0.0395 + 20 +0.0063 + 10 +0.0395 + 20 +-0.0063 + 0 +LWPOLYLINE + 5 +31 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0252 + 20 +0.0311 + 10 +0.0315 + 20 +0.0389 + 10 +0.0179 + 20 +0.0467 + 10 +0.0143 + 20 +0.0373 + 10 +0.0252 + 20 +0.0311 + 0 +LWPOLYLINE + 5 +32 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0143 + 20 +0.0373 + 10 +-0.0179 + 20 +0.0467 + 10 +-0.0315 + 20 +0.0389 + 10 +-0.0252 + 20 +0.0311 + 10 +-0.0143 + 20 +0.0373 + 0 +LWPOLYLINE + 5 +33 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0395 + 20 +0.0063 + 10 +-0.0494 + 20 +0.0078 + 10 +-0.0494 + 20 +-0.0078 + 10 +-0.0395 + 20 +-0.0063 + 10 +-0.0395 + 20 +0.0063 + 0 +LWPOLYLINE + 5 +34 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0252 + 20 +-0.0311 + 10 +-0.0315 + 20 +-0.0389 + 10 +-0.0179 + 20 +-0.0467 + 10 +-0.0143 + 20 +-0.0373 + 10 +-0.0252 + 20 +-0.0311 + 0 +LWPOLYLINE + 5 +35 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0143 + 20 +-0.0373 + 10 +0.0179 + 20 +-0.0467 + 10 +0.0315 + 20 +-0.0389 + 10 +0.0252 + 20 +-0.0311 + 10 +0.0143 + 20 +-0.0373 + 0 +LWPOLYLINE + 5 +36 +330 +17 +100 +AcDbEntity + 8 +material_air +100 +AcDbPolyline + 90 +19 + 70 +0 + 10 +0.04 + 20 +0.0 + 10 +0.0376 + 20 +0.0137 + 10 +0.0306 + 20 +0.0257 + 10 +0.02 + 20 +0.0346 + 10 +0.0069 + 20 +0.0394 + 10 +-0.0069 + 20 +0.0394 + 10 +-0.02 + 20 +0.0346 + 10 +-0.0306 + 20 +0.0257 + 10 +-0.0376 + 20 +0.0137 + 10 +-0.04 + 20 +0.0 + 10 +-0.0376 + 20 +-0.0137 + 10 +-0.0306 + 20 +-0.0257 + 10 +-0.02 + 20 +-0.0346 + 10 +-0.0069 + 20 +-0.0394 + 10 +0.0069 + 20 +-0.0394 + 10 +0.02 + 20 +-0.0346 + 10 +0.0306 + 20 +-0.0257 + 10 +0.0376 + 20 +-0.0137 + 10 +0.04 + 20 +0.0 + 0 +LWPOLYLINE + 5 +37 +330 +17 +100 +AcDbEntity + 8 +material_rotor_core +100 +AcDbPolyline + 90 +15 + 70 +0 + 10 +0.0232 + 20 +0.0 + 10 +0.0209 + 20 +0.0101 + 10 +0.0145 + 20 +0.0181 + 10 +0.0052 + 20 +0.0226 + 10 +-0.0052 + 20 +0.0226 + 10 +-0.0145 + 20 +0.0181 + 10 +-0.0209 + 20 +0.0101 + 10 +-0.0232 + 20 +0.0 + 10 +-0.0209 + 20 +-0.0101 + 10 +-0.0145 + 20 +-0.0181 + 10 +-0.0052 + 20 +-0.0226 + 10 +0.0052 + 20 +-0.0226 + 10 +0.0145 + 20 +-0.0181 + 10 +0.0209 + 20 +-0.0101 + 10 +0.0232 + 20 +0.0 + 0 +LWPOLYLINE + 5 +38 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0081 + 20 +-0.0008 + 10 +0.0226 + 20 +-0.0024 + 10 +0.0226 + 20 +0.0024 + 10 +0.0081 + 20 +0.0008 + 10 +0.0081 + 20 +-0.0008 + 0 +LWPOLYLINE + 5 +39 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0048 + 20 +0.0066 + 10 +0.0134 + 20 +0.0184 + 10 +0.0092 + 20 +0.0208 + 10 +0.0033 + 20 +0.0074 + 10 +0.0048 + 20 +0.0066 + 0 +LWPOLYLINE + 5 +3A +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0033 + 20 +0.0074 + 10 +-0.0092 + 20 +0.0208 + 10 +-0.0134 + 20 +0.0184 + 10 +-0.0048 + 20 +0.0066 + 10 +-0.0033 + 20 +0.0074 + 0 +LWPOLYLINE + 5 +3B +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0081 + 20 +0.0008 + 10 +-0.0226 + 20 +0.0024 + 10 +-0.0226 + 20 +-0.0024 + 10 +-0.0081 + 20 +-0.0008 + 10 +-0.0081 + 20 +0.0008 + 0 +LWPOLYLINE + 5 +3C +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0048 + 20 +-0.0066 + 10 +-0.0134 + 20 +-0.0184 + 10 +-0.0092 + 20 +-0.0208 + 10 +-0.0033 + 20 +-0.0074 + 10 +-0.0048 + 20 +-0.0066 + 0 +LWPOLYLINE + 5 +3D +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0033 + 20 +-0.0074 + 10 +0.0092 + 20 +-0.0208 + 10 +0.0134 + 20 +-0.0184 + 10 +0.0048 + 20 +-0.0066 + 10 +0.0033 + 20 +-0.0074 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +40 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:03:02.343882+00:00 + 0 +DICTIONARYVAR + 5 +40 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:03:02.353736+00:00 + 0 +ENDSEC + 0 +EOF diff --git a/docs/assets/dxf/induction_demo/rotor_bars.dxf b/docs/assets/dxf/induction_demo/rotor_bars.dxf new file mode 100644 index 0000000..f28318a --- /dev/null +++ b/docs/assets/dxf/induction_demo/rotor_bars.dxf @@ -0,0 +1,3260 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460989.8777777776 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460989.8777777776 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +38 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{F45C26B3-3628-4FD1-BA9B-C1D7C8895113} + 9 +$VERSIONGUID + 2 +{9D6E6FB8-C7E7-4872-8527-2BAAD026CA87} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +35 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +36 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +LWPOLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0081 + 20 +-0.0008 + 10 +0.0226 + 20 +-0.0024 + 10 +0.0226 + 20 +0.0024 + 10 +0.0081 + 20 +0.0008 + 10 +0.0081 + 20 +-0.0008 + 0 +LWPOLYLINE + 5 +30 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0048 + 20 +0.0066 + 10 +0.0134 + 20 +0.0184 + 10 +0.0092 + 20 +0.0208 + 10 +0.0033 + 20 +0.0074 + 10 +0.0048 + 20 +0.0066 + 0 +LWPOLYLINE + 5 +31 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0033 + 20 +0.0074 + 10 +-0.0092 + 20 +0.0208 + 10 +-0.0134 + 20 +0.0184 + 10 +-0.0048 + 20 +0.0066 + 10 +-0.0033 + 20 +0.0074 + 0 +LWPOLYLINE + 5 +32 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0081 + 20 +0.0008 + 10 +-0.0226 + 20 +0.0024 + 10 +-0.0226 + 20 +-0.0024 + 10 +-0.0081 + 20 +-0.0008 + 10 +-0.0081 + 20 +0.0008 + 0 +LWPOLYLINE + 5 +33 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +-0.0048 + 20 +-0.0066 + 10 +-0.0134 + 20 +-0.0184 + 10 +-0.0092 + 20 +-0.0208 + 10 +-0.0033 + 20 +-0.0074 + 10 +-0.0048 + 20 +-0.0066 + 0 +LWPOLYLINE + 5 +34 +330 +17 +100 +AcDbEntity + 8 +material_rotor_bar +100 +AcDbPolyline + 90 +5 + 70 +0 + 10 +0.0033 + 20 +-0.0074 + 10 +0.0092 + 20 +-0.0208 + 10 +0.0134 + 20 +-0.0184 + 10 +0.0048 + 20 +-0.0066 + 10 +0.0033 + 20 +-0.0074 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +37 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.686081+00:00 + 0 +DICTIONARYVAR + 5 +37 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.688992+00:00 + 0 +ENDSEC + 0 +EOF diff --git a/docs/assets/dxf/induction_demo/rotor_core.dxf b/docs/assets/dxf/induction_demo/rotor_core.dxf new file mode 100644 index 0000000..5331008 --- /dev/null +++ b/docs/assets/dxf/induction_demo/rotor_core.dxf @@ -0,0 +1,3120 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460989.8777777776 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460989.8777777776 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +33 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{B058C782-883E-4E7C-9C3C-9170640B6BCD} + 9 +$VERSIONGUID + 2 +{9BD3612E-653F-4661-80EE-D1BC492D37FA} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +30 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +31 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +LWPOLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +material_rotor_core +100 +AcDbPolyline + 90 +15 + 70 +0 + 10 +0.0232 + 20 +0.0 + 10 +0.0209 + 20 +0.0101 + 10 +0.0145 + 20 +0.0181 + 10 +0.0052 + 20 +0.0226 + 10 +-0.0052 + 20 +0.0226 + 10 +-0.0145 + 20 +0.0181 + 10 +-0.0209 + 20 +0.0101 + 10 +-0.0232 + 20 +0.0 + 10 +-0.0209 + 20 +-0.0101 + 10 +-0.0145 + 20 +-0.0181 + 10 +-0.0052 + 20 +-0.0226 + 10 +0.0052 + 20 +-0.0226 + 10 +0.0145 + 20 +-0.0181 + 10 +0.0209 + 20 +-0.0101 + 10 +0.0232 + 20 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +32 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.667315+00:00 + 0 +DICTIONARYVAR + 5 +32 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.670715+00:00 + 0 +ENDSEC + 0 +EOF diff --git a/docs/assets/dxf/induction_demo/stator_only.dxf b/docs/assets/dxf/induction_demo/stator_only.dxf new file mode 100644 index 0000000..59c42e9 --- /dev/null +++ b/docs/assets/dxf/induction_demo/stator_only.dxf @@ -0,0 +1,3160 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460989.8777777776 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460989.8777777776 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +33 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{D42408CD-9EED-4F0D-8F20-79DB8556A4A6} + 9 +$VERSIONGUID + 2 +{094D7FB6-E9B8-402F-A092-AD2C044ABDF6} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +30 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +31 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +LWPOLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +material_stator_steel +100 +AcDbPolyline + 90 +25 + 70 +0 + 10 +0.055 + 20 +0.0 + 10 +0.0531 + 20 +0.0142 + 10 +0.0476 + 20 +0.0275 + 10 +0.0389 + 20 +0.0389 + 10 +0.0275 + 20 +0.0476 + 10 +0.0142 + 20 +0.0531 + 10 +0.0 + 20 +0.055 + 10 +-0.0142 + 20 +0.0531 + 10 +-0.0275 + 20 +0.0476 + 10 +-0.0389 + 20 +0.0389 + 10 +-0.0476 + 20 +0.0275 + 10 +-0.0531 + 20 +0.0142 + 10 +-0.055 + 20 +0.0 + 10 +-0.0531 + 20 +-0.0142 + 10 +-0.0476 + 20 +-0.0275 + 10 +-0.0389 + 20 +-0.0389 + 10 +-0.0275 + 20 +-0.0476 + 10 +-0.0142 + 20 +-0.0531 + 10 +0.0 + 20 +-0.055 + 10 +0.0142 + 20 +-0.0531 + 10 +0.0275 + 20 +-0.0476 + 10 +0.0389 + 20 +-0.0389 + 10 +0.0476 + 20 +-0.0275 + 10 +0.0531 + 20 +-0.0142 + 10 +0.055 + 20 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +32 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.640583+00:00 + 0 +DICTIONARYVAR + 5 +32 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.3 @ 2025-11-09T21:04:00.646864+00:00 + 0 +ENDSEC + 0 +EOF diff --git a/docs/developer-guide/implementation/AGENTS.md b/docs/developer-guide/implementation/AGENTS.md new file mode 100644 index 0000000..a9eb1a0 --- /dev/null +++ b/docs/developer-guide/implementation/AGENTS.md @@ -0,0 +1,14 @@ +# Implementation Progress Docs Notes + +- This folder hosts running logs that track implementation efforts for major + epics (solver, GUI, etc.). Keep entries factual and date-agnostic so the + narrative remains evergreen. +- When you land new capabilities, add concise bullets under the relevant + sections rather than rewriting history; older entries provide context for + future contributors. +- If you introduce a new epic, create a sibling progress file and reference it + from the MkDocs navigation (`mkdocs.yml`). +- Tests or code changes that the documentation references should be cited with + file paths or module names where possible. +- Avoid embedding binary artefacts or screenshots here—link to reproducible + commands or test cases instead. diff --git a/docs/developer-guide/implementation/flask-gui-progress.md b/docs/developer-guide/implementation/flask-gui-progress.md index 85dfc38..1ba5aac 100644 --- a/docs/developer-guide/implementation/flask-gui-progress.md +++ b/docs/developer-guide/implementation/flask-gui-progress.md @@ -22,15 +22,43 @@ on the GUI-specific milestones. endpoint payloads without invoking the real solver binary. - Documented usage (`docs/user-guide/gui_flask.md`) and linked the new page into the MkDocs navigation. +- **Stage 1 UI overhaul**: + - Split the main page into dedicated geometry, control, progress, downloads, + and visualisation panels with placeholders for future DXF/animation work. + - Introduced a CAD-style top bar and workflow sidebar so navigation feels + closer to desktop tooling while leaving hooks for future menus. + - Added geometry preview rendering on demand plus live in-process and final + field-map visualisation streamed via SSE. + - Introduced configurable plotting controls (vector scaling, density, + overlays, logarithmic modes) that regenerate Matplotlib renders on request. + - Persisted scenario uploads as per-session projects, enabling preview and run + actions without re-uploading; added File-menu routes for exporting or + clearing the workspace. + - Expanded automated coverage for preview flows, solver output detection, + project reuse, and the visualisation endpoint to keep regression protection + high. + - Added helper scripts (`scripts/setup_gui_env.sh` and + `scripts/maintain_gui_env.sh`) so contributors can bootstrap dependencies, + prune artefacts, and monitor Python package freshness locally. +- Added a DXF workspace with multi-file uploads, layer assignment, combined + previews, and scenario-to-DXF export support; introduced `python/gui/dxf_utils.py` + to encapsulate parsing and rendering logic and exercised the flow in + `tests/test_gui_flask.py`. +- Hardened field-map discovery so relative solver outputs resolve against the + runtime working directory, streamed intermediate frames into a playback + gallery with animation controls, and published an end-to-end induction motor + workflow (scenario, DXFs, documentation) to guide future additions. ## In-flight / next steps -- DXF ingestion is still stubbed. Once the geometry-to-scenario converter is - ready we can replace the placeholder guard in `/upload` with real processing. +- Broaden DXF support beyond lines/polylines/circles (splines, blocks) and add + utilities that map layer selections back into scenario JSON automatically. - The progress parser currently looks for percentage tokens in stdout; refining this to understand the solver's exact log format will improve the progress bar fidelity. -- Consider persisting additional solver outputs (e.g., field map CSVs) to the - downloads list once the CLI contract is finalised. +- Surface more solver artefacts (line probes, streamlines) alongside field maps + once those exporters are exercised in real scenarios. - Harden long-running process management for multiple users (per-session queues instead of global state) if the GUI graduates beyond a single-user demo. +- Add screenshot-driven regression tests for the Bootstrap layout when we wire + up a lightweight browser harness. diff --git a/docs/user-guide/gui-workflows/induction-demo.md b/docs/user-guide/gui-workflows/induction-demo.md new file mode 100644 index 0000000..4bada48 --- /dev/null +++ b/docs/user-guide/gui-workflows/induction-demo.md @@ -0,0 +1,99 @@ +# Induction motor GUI workflow + +This walkthrough demonstrates how to assemble the bundled three-phase induction +motor demo inside the Flask GUI, run the transient solve, and review the +resulting field maps and animation. It assumes you have already built +`motor_sim` and started the Flask server as described in the +[GUI overview](../gui_flask.md). + +## Assets + +| Purpose | File | Notes | +| --- | --- | --- | +| Scenario JSON | [`inputs/induction_gui_demo.json`](../../inputs/induction_gui_demo.json) | Defines the transient spin-up with field-map, VTK series, and probe outputs. | +| Domain outline | [`docs/assets/dxf/induction_demo/domain.dxf`](../../assets/dxf/induction_demo/domain.dxf) | Axis-aligned boundary for the rectangular workspace. | +| Stator laminations | [`docs/assets/dxf/induction_demo/stator_only.dxf`](../../assets/dxf/induction_demo/stator_only.dxf) | Single layer containing the stator steel outline. | +| Rotor back-iron | [`docs/assets/dxf/induction_demo/rotor_core.dxf`](../../assets/dxf/induction_demo/rotor_core.dxf) | Rotor core polygon. | +| Rotor bars | [`docs/assets/dxf/induction_demo/rotor_bars.dxf`](../../assets/dxf/induction_demo/rotor_bars.dxf) | Conductor bars, one polyline per bar. | +| Air references | [`docs/assets/dxf/induction_demo/air_regions.dxf`](../../assets/dxf/induction_demo/air_regions.dxf) | Convenience overlay to confirm the air gap and slot cavities. | + +> Tip: if you tweak the scenario and want to regenerate the DXF set, choose +> **File → Export DXF set…** after the run. The GUI will emit fresh DXFs for the +> loaded scenario using the same layering rules described above. + +## Step 1 – Prime the workspace + +1. Launch the server with `python -m python.gui.app_flask` and open the + interface in your browser. +2. In the **File** menu choose **New project** if a previous session is still + loaded. +3. Download the assets listed above (or keep the repository checked out so the + relative links work directly). + +## Step 2 – Import CAD layers + +Use the **CAD workspace** panel to upload each DXF. The recommended order helps +keep the combined preview tidy, but the GUI will happily accept any sequence. + +| Upload order | Layer selection | Suggested category | +| --- | --- | --- | +| `domain.dxf` | Single `domain` layer | `domain` | +| `stator_only.dxf` | Entire layer | `material` (stator steel) | +| `rotor_core.dxf` | Entire layer | `material` (rotor core) | +| `rotor_bars.dxf` | Entire layer | `wire` | +| `air_regions.dxf` | Optional visual guide | `structural` | + +After each upload, expand the card, tick the `Include` checkboxes, and assign +categories from the drop-down. Click **Update mapping** to persist the +selection, then confirm the coloured overlay in the **Combined preview** on the +right. When everything is aligned the stator/rotor teeth should nest cleanly +within the domain boundary. + +## Step 3 – Load the scenario + +1. Scroll to **Simulation setup** and use **Browse…** to select + `inputs/induction_gui_demo.json`. +2. Leave the solver defaults (CG, `1e-6` tolerance, `20000` max iterations) + intact. The scenario already requests the `induction_motor_field` field-map + along with the VTK series and probes, so the `--outputs` box can remain + blank. +3. Click **Run simulation**. The progress card and live log will appear and the + **Run** button will grey out while the solver works. + +## Step 4 – Monitor progress and animation + +- The live log shows each timeline frame as it converges. When the solver writes + a field map (e.g. `Frame 0: wrote field_map 'induction_motor_field' …`) the + GUI renders a preview immediately. +- The **Results & visuals** card keeps a gallery of these frames. Use the + **Play** button to loop through the snapshots, **Prev/Next** to step manually, + or drag the slider to inspect a specific frame. The caption updates with the + frame index so you can correlate it with the log. +- You can re-render the final field map with different overlays at any time by + adjusting the controls (vector scaling, log floor, streamlines) and clicking + **Update visualisation**. + +## Step 5 – Collect artefacts + +When the run completes successfully the **Downloads** list exposes: + +- The uploaded scenario JSON (with any edits from the DXF workspace preserved). +- The solver log. +- `outputs/induction_motor_field.csv` plus the other CSV/VTP artefacts declared + in the scenario. +- A PNG of the default field-map render. + +These files, along with the animation controls, are enough to document the +induction motor run or to feed the CSVs into the downstream plotting tools such +as `python/animate_three_phase.py` if you need a high-resolution movie. + +## Next steps + +- Save the current project through **File → Save scenario…** if you tweak the + geometry or solver settings. The saved JSON can be re-uploaded later to keep + iterating. +- Use **File → Export DXF set…** to share the geometry with teammates or to + seed another CAD package. +- For heavier visualisations (rotor slot colouring, full GIF exports) hand the + downloads to the existing scripts in `python/`—the GUI keeps the workspace + clean while providing a quick feedback loop. diff --git a/docs/user-guide/gui_flask.md b/docs/user-guide/gui_flask.md index 3792f3a..e9227cd 100644 --- a/docs/user-guide/gui_flask.md +++ b/docs/user-guide/gui_flask.md @@ -7,10 +7,10 @@ prototype while staying close to the underlying command-line workflow. ## Requirements - A built `motor_sim` executable in `./build/motor_sim`. -- The devcontainer (or local virtual environment) must include Flask and its - lightweight companions. The repository's `.devcontainer/Dockerfile` installs - Flask, Flask-CORS, NumPy, Matplotlib, and `ezdxf` so the GUI has everything it - needs when launched inside Codespaces. +- Flask, Matplotlib, NumPy, and ezdxf available in the active Python + environment. Execute `./scripts/setup_gui_env.sh` to install the toolchain + (and create the GUI runtime folders) when a pre-provisioned devcontainer is + not available. ## Starting the server @@ -29,30 +29,88 @@ prefer Flask's CLI wrapper. ## Using the interface -1. Upload a scenario JSON file via the **Scenario file** input. (DXF uploads are - reserved for a future update and currently return a validation error.) -2. Pick a solver (`cg` or `sor`), adjust the tolerance and maximum iteration - count if required, and optionally pass a value for `--outputs`. -3. Click **Run simulation**. The form disables itself, the **Stop** button - becomes available, and a progress card appears. -4. The server streams the solver's stdout via Server-Sent Events (SSE). The - progress bar reacts to any percentage tokens in the log, and the log panel - fills with the live output. -5. When the run finishes, the log persists and a **Downloads** card appears with - links to the uploaded scenario and the captured log file. The log is useful - for sharing solver traces without leaving the browser. -6. Click **Run simulation** again to process another scenario. Only one solve is - allowed at a time; the interface reports an error if you attempt to start a - second run before the first completes. - -Use the **Stop** button to request early termination. The server sends -`terminate()` to the subprocess and the final log entry marks the stop request. -Depending on solver state, expect a short delay while the child process exits. +The CAD-inspired layout splits the page into a top navigation bar, a workflow +sidebar, and content panels for each stage. The **File** menu exposes +project-wide actions (**New project** clears the workspace, **Save scenario…** +downloads the current JSON, **Export DXF set…** produces layered DXFs for the +current scenario), while the sidebar links jump to the key panels without +leaving the page. + +Looking for a guided example? Follow the +[Induction motor GUI workflow](gui-workflows/induction-demo.md) to import the +bundled DXF set, run the transient spin-up, and inspect the field-map gallery. + +1. Visit the **CAD workspace** to import DXF files when you want to assemble or + revise geometry. Each upload is summarised by layer; select the layers to + include, assign a category (material, magnet, wire, etc.), and click **Update + mapping** to refresh the combined preview. The preview colours match the + chosen categories so you can confirm alignments before committing to a + scenario JSON. +2. Use the **Simulation setup** panel to upload (or re-upload) a scenario JSON + when you are ready to run the solver. The form preserves solver defaults so + you only need to change the file when switching projects. Optional solver + arguments—`cg`/`sor`, tolerance, iteration limit, and `--outputs`—map directly + onto the `motor_sim` CLI. +3. Choose **Preview geometry** to render the domain outline without solving. + Previews stay cached with the project and coexist with the DXF preview so you + can compare imported geometry with the scenario domain. +4. Click **Run simulation** to launch `motor_sim`. The run button disables + itself, the **Stop** button becomes active, and the **Progress**/**Solver log** + cards appear. Logs stream via server-sent events, avoiding polling. +5. Monitor the run: percentage tokens update the progress bar, log entries append + live, and any `field_map` outputs mentioned in stdout trigger refreshed images + in the **Results visualisation** panel. Intermediate renders replace the + preview automatically. +6. Once the solver exits, the **Downloads** panel lists the uploaded scenario, + the log file, the combined DXF exports (when requested), and any field-map + artefacts produced by the solver. The visualisation panel shows the final + render and stays wired to the plotting controls so you can iterate on overlays + without rerunning the simulation. + +Only one solve is allowed at a time; the interface reports an error if you try +to launch a second run before the first finishes. Use **Stop** to request +termination—`terminate()` is sent to the subprocess and the log records the +request before the child exits. + +### Environment upkeep + +Generated artefacts accumulate under `python/gui/uploads` and +`python/gui/results`. Run `./scripts/maintain_gui_env.sh` periodically (set +`KEEP_DAYS` or pass a day count to keep more history) to prune stale files and +surface any outdated Python packages. + +### Visualisation controls + +The results card includes a lightweight form for regenerating the field-map +image without leaving the browser: + +- **Vector scaling**: switch between linear arrows, logarithmic scaling, or hide + vectors entirely. +- **Colour scale**: toggle between linear and logarithmic magnitude mapping. +- **Arrow skip**: control the quiver density (defaults to every fourth sample). +- **Log floor / Vector floor**: clamp low values when using logarithmic modes. +- **Region outlines / Streamlines**: enable or disable material/magnet borders + and streamline overlays. + +Choose the desired parameters and click **Update visualisation**. The server +re-renders the image with Matplotlib and returns a fresh PNG via the +`/visualization.png` endpoint, keeping the UI responsive with minimal +JavaScript. + +During a simulation the GUI also builds a frame gallery from each reported +`field_map`. Use the playback controls above the image to loop the snapshots, +step frame-by-frame, or drag the slider to inspect a single moment. The caption +updates with the frame index, making it easy to cross-reference the solver log +without leaving the page. ## Known limitations -- DXF geometry ingestion is stubbed; use JSON scenarios generated via the - existing Python helpers until the converter is available. +- DXF ingestion currently supports LINE/POLYLINE/LWPOLYLINE and CIRCLE entities. + Splines, text, and block references are ignored. Use your CAD tool to explode + complex geometry before uploading. +- Preview and visualisation rendering rely on Matplotlib, NumPy, and ezdxf. + These dependencies ship with the devcontainer, but a custom environment must + provide the same stack. - The progress parser uses a simple percentage heuristic. If the solver log does not emit percentage tokens the bar will stay at 0 % until the run completes. - The current prototype keeps global state and targets single-user usage. A diff --git a/inputs/induction_gui_demo.json b/inputs/induction_gui_demo.json new file mode 100644 index 0000000..3fd3a4a --- /dev/null +++ b/inputs/induction_gui_demo.json @@ -0,0 +1,1389 @@ +{ + "version": "0.2", + "units": "SI", + "domain": { + "Lx": 0.14, + "Ly": 0.14, + "nx": 65, + "ny": 65 + }, + "boundary": { + "type": "neumann" + }, + "materials": [ + { + "name": "air", + "mu_r": 1.0, + "sigma": 0.0 + }, + { + "name": "stator_steel", + "mu_r": 600.0, + "sigma": 0.0 + }, + { + "name": "rotor_core", + "mu_r": 400.0, + "sigma": 500000.0 + }, + { + "name": "rotor_bar", + "mu_r": 1.0, + "sigma": 18000000.0 + } + ], + "regions": [ + { + "type": "uniform", + "material": "air" + }, + { + "type": "polygon", + "material": "stator_steel", + "vertices": [ + [ + 0.055, + 0.0 + ], + [ + 0.0531, + 0.0142 + ], + [ + 0.0476, + 0.0275 + ], + [ + 0.0389, + 0.0389 + ], + [ + 0.0275, + 0.0476 + ], + [ + 0.0142, + 0.0531 + ], + [ + 0.0, + 0.055 + ], + [ + -0.0142, + 0.0531 + ], + [ + -0.0275, + 0.0476 + ], + [ + -0.0389, + 0.0389 + ], + [ + -0.0476, + 0.0275 + ], + [ + -0.0531, + 0.0142 + ], + [ + -0.055, + 0.0 + ], + [ + -0.0531, + -0.0142 + ], + [ + -0.0476, + -0.0275 + ], + [ + -0.0389, + -0.0389 + ], + [ + -0.0275, + -0.0476 + ], + [ + -0.0142, + -0.0531 + ], + [ + 0.0, + -0.055 + ], + [ + 0.0142, + -0.0531 + ], + [ + 0.0275, + -0.0476 + ], + [ + 0.0389, + -0.0389 + ], + [ + 0.0476, + -0.0275 + ], + [ + 0.0531, + -0.0142 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + 0.0395, + -0.0063 + ], + [ + 0.0494, + -0.0078 + ], + [ + 0.0494, + 0.0078 + ], + [ + 0.0395, + 0.0063 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + 0.0252, + 0.0311 + ], + [ + 0.0315, + 0.0389 + ], + [ + 0.0179, + 0.0467 + ], + [ + 0.0143, + 0.0373 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + -0.0143, + 0.0373 + ], + [ + -0.0179, + 0.0467 + ], + [ + -0.0315, + 0.0389 + ], + [ + -0.0252, + 0.0311 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + -0.0395, + 0.0063 + ], + [ + -0.0494, + 0.0078 + ], + [ + -0.0494, + -0.0078 + ], + [ + -0.0395, + -0.0063 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + -0.0252, + -0.0311 + ], + [ + -0.0315, + -0.0389 + ], + [ + -0.0179, + -0.0467 + ], + [ + -0.0143, + -0.0373 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + 0.0143, + -0.0373 + ], + [ + 0.0179, + -0.0467 + ], + [ + 0.0315, + -0.0389 + ], + [ + 0.0252, + -0.0311 + ] + ] + }, + { + "type": "polygon", + "material": "air", + "vertices": [ + [ + 0.04, + 0.0 + ], + [ + 0.0376, + 0.0137 + ], + [ + 0.0306, + 0.0257 + ], + [ + 0.02, + 0.0346 + ], + [ + 0.0069, + 0.0394 + ], + [ + -0.0069, + 0.0394 + ], + [ + -0.02, + 0.0346 + ], + [ + -0.0306, + 0.0257 + ], + [ + -0.0376, + 0.0137 + ], + [ + -0.04, + 0.0 + ], + [ + -0.0376, + -0.0137 + ], + [ + -0.0306, + -0.0257 + ], + [ + -0.02, + -0.0346 + ], + [ + -0.0069, + -0.0394 + ], + [ + 0.0069, + -0.0394 + ], + [ + 0.02, + -0.0346 + ], + [ + 0.0306, + -0.0257 + ], + [ + 0.0376, + -0.0137 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_core", + "vertices": [ + [ + 0.0232, + 0.0 + ], + [ + 0.0209, + 0.0101 + ], + [ + 0.0145, + 0.0181 + ], + [ + 0.0052, + 0.0226 + ], + [ + -0.0052, + 0.0226 + ], + [ + -0.0145, + 0.0181 + ], + [ + -0.0209, + 0.0101 + ], + [ + -0.0232, + 0.0 + ], + [ + -0.0209, + -0.0101 + ], + [ + -0.0145, + -0.0181 + ], + [ + -0.0052, + -0.0226 + ], + [ + 0.0052, + -0.0226 + ], + [ + 0.0145, + -0.0181 + ], + [ + 0.0209, + -0.0101 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_bar", + "vertices": [ + [ + 0.0081, + -0.0008 + ], + [ + 0.0226, + -0.0024 + ], + [ + 0.0226, + 0.0024 + ], + [ + 0.0081, + 0.0008 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_bar", + "vertices": [ + [ + 0.0048, + 0.0066 + ], + [ + 0.0134, + 0.0184 + ], + [ + 0.0092, + 0.0208 + ], + [ + 0.0033, + 0.0074 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_bar", + "vertices": [ + [ + -0.0033, + 0.0074 + ], + [ + -0.0092, + 0.0208 + ], + [ + -0.0134, + 0.0184 + ], + [ + -0.0048, + 0.0066 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_bar", + "vertices": [ + [ + -0.0081, + 0.0008 + ], + [ + -0.0226, + 0.0024 + ], + [ + -0.0226, + -0.0024 + ], + [ + -0.0081, + -0.0008 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_bar", + "vertices": [ + [ + -0.0048, + -0.0066 + ], + [ + -0.0134, + -0.0184 + ], + [ + -0.0092, + -0.0208 + ], + [ + -0.0033, + -0.0074 + ] + ] + }, + { + "type": "polygon", + "material": "rotor_bar", + "vertices": [ + [ + 0.0033, + -0.0074 + ], + [ + 0.0092, + -0.0208 + ], + [ + 0.0134, + -0.0184 + ], + [ + 0.0048, + -0.0066 + ] + ] + } + ], + "magnet_regions": [], + "rotors": [ + { + "name": "induction_rotor", + "pivot": [ + 0.0, + 0.0 + ], + "polygons": [ + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ] + } + ], + "sources": [ + { + "type": "current_region", + "id": "A_pos", + "label": "Phase A+", + "phase": "A", + "orientation": 1.0, + "vertices": [ + [ + 0.0395, + -0.0063 + ], + [ + 0.0494, + -0.0078 + ], + [ + 0.0494, + 0.0078 + ], + [ + 0.0395, + 0.0063 + ] + ], + "I": 0.0, + "turns": 50.0, + "fill_fraction": 0.55 + }, + { + "type": "current_region", + "id": "B_neg", + "label": "Phase B-", + "phase": "B", + "orientation": -1.0, + "vertices": [ + [ + 0.0252, + 0.0311 + ], + [ + 0.0315, + 0.0389 + ], + [ + 0.0179, + 0.0467 + ], + [ + 0.0143, + 0.0373 + ] + ], + "I": 0.0, + "turns": 50.0, + "fill_fraction": 0.55 + }, + { + "type": "current_region", + "id": "C_pos", + "label": "Phase C+", + "phase": "C", + "orientation": 1.0, + "vertices": [ + [ + -0.0143, + 0.0373 + ], + [ + -0.0179, + 0.0467 + ], + [ + -0.0315, + 0.0389 + ], + [ + -0.0252, + 0.0311 + ] + ], + "I": 0.0, + "turns": 50.0, + "fill_fraction": 0.55 + }, + { + "type": "current_region", + "id": "A_neg", + "label": "Phase A-", + "phase": "A", + "orientation": -1.0, + "vertices": [ + [ + -0.0395, + 0.0063 + ], + [ + -0.0494, + 0.0078 + ], + [ + -0.0494, + -0.0078 + ], + [ + -0.0395, + -0.0063 + ] + ], + "I": 0.0, + "turns": 50.0, + "fill_fraction": 0.55 + }, + { + "type": "current_region", + "id": "B_pos", + "label": "Phase B+", + "phase": "B", + "orientation": 1.0, + "vertices": [ + [ + -0.0252, + -0.0311 + ], + [ + -0.0315, + -0.0389 + ], + [ + -0.0179, + -0.0467 + ], + [ + -0.0143, + -0.0373 + ] + ], + "I": 0.0, + "turns": 50.0, + "fill_fraction": 0.55 + }, + { + "type": "current_region", + "id": "C_neg", + "label": "Phase C-", + "phase": "C", + "orientation": -1.0, + "vertices": [ + [ + 0.0143, + -0.0373 + ], + [ + 0.0179, + -0.0467 + ], + [ + 0.0315, + -0.0389 + ], + [ + 0.0252, + -0.0311 + ] + ], + "I": 0.0, + "turns": 50.0, + "fill_fraction": 0.55 + } + ], + "mechanical": { + "rotors": [ + { + "name": "induction_rotor", + "inertia": 0.0011, + "damping": 0.0001, + "load_torque": 0.08, + "initial_angle_deg": -20.0, + "initial_speed_rpm": 0.0, + "torque_probe": "induction_motor_torque" + } + ] + }, + "transient": { + "dt": 0.0014, + "n_steps": 36 + }, + "timeline": [ + { + "t": 0.0, + "phase_currents": { + "A": 0.0, + "B": -36.3731, + "C": 36.3731 + } + }, + { + "t": 0.0014, + "phase_currents": { + "A": 21.0, + "B": -42.0, + "C": 21.0 + } + }, + { + "t": 0.0028, + "phase_currents": { + "A": 36.3731, + "B": -36.3731, + "C": 0.0 + } + }, + { + "t": 0.0042, + "phase_currents": { + "A": 42.0, + "B": -21.0, + "C": -21.0 + } + }, + { + "t": 0.0056, + "phase_currents": { + "A": 36.3731, + "B": 0.0, + "C": -36.3731 + } + }, + { + "t": 0.0069, + "phase_currents": { + "A": 21.0, + "B": 21.0, + "C": -42.0 + } + }, + { + "t": 0.0083, + "phase_currents": { + "A": 0.0, + "B": 36.3731, + "C": -36.3731 + } + }, + { + "t": 0.0097, + "phase_currents": { + "A": -21.0, + "B": 42.0, + "C": -21.0 + } + }, + { + "t": 0.0111, + "phase_currents": { + "A": -36.3731, + "B": 36.3731, + "C": 0.0 + } + }, + { + "t": 0.0125, + "phase_currents": { + "A": -42.0, + "B": 21.0, + "C": 21.0 + } + }, + { + "t": 0.0139, + "phase_currents": { + "A": -36.3731, + "B": 0.0, + "C": 36.3731 + } + }, + { + "t": 0.0153, + "phase_currents": { + "A": -21.0, + "B": -21.0, + "C": 42.0 + } + }, + { + "t": 0.0167, + "phase_currents": { + "A": 0.0, + "B": -36.3731, + "C": 36.3731 + } + }, + { + "t": 0.0181, + "phase_currents": { + "A": 21.0, + "B": -42.0, + "C": 21.0 + } + }, + { + "t": 0.0194, + "phase_currents": { + "A": 36.3731, + "B": -36.3731, + "C": 0.0 + } + }, + { + "t": 0.0208, + "phase_currents": { + "A": 42.0, + "B": -21.0, + "C": -21.0 + } + }, + { + "t": 0.0222, + "phase_currents": { + "A": 36.3731, + "B": 0.0, + "C": -36.3731 + } + }, + { + "t": 0.0236, + "phase_currents": { + "A": 21.0, + "B": 21.0, + "C": -42.0 + } + }, + { + "t": 0.025, + "phase_currents": { + "A": 0.0, + "B": 36.3731, + "C": -36.3731 + } + }, + { + "t": 0.0264, + "phase_currents": { + "A": -21.0, + "B": 42.0, + "C": -21.0 + } + }, + { + "t": 0.0278, + "phase_currents": { + "A": -36.3731, + "B": 36.3731, + "C": 0.0 + } + }, + { + "t": 0.0292, + "phase_currents": { + "A": -42.0, + "B": 21.0, + "C": 21.0 + } + }, + { + "t": 0.0306, + "phase_currents": { + "A": -36.3731, + "B": 0.0, + "C": 36.3731 + } + }, + { + "t": 0.0319, + "phase_currents": { + "A": -21.0, + "B": -21.0, + "C": 42.0 + } + }, + { + "t": 0.0333, + "phase_currents": { + "A": 0.0, + "B": -36.3731, + "C": 36.3731 + } + }, + { + "t": 0.0347, + "phase_currents": { + "A": 21.0, + "B": -42.0, + "C": 21.0 + } + }, + { + "t": 0.0361, + "phase_currents": { + "A": 36.3731, + "B": -36.3731, + "C": 0.0 + } + }, + { + "t": 0.0375, + "phase_currents": { + "A": 42.0, + "B": -21.0, + "C": -21.0 + } + }, + { + "t": 0.0389, + "phase_currents": { + "A": 36.3731, + "B": 0.0, + "C": -36.3731 + } + }, + { + "t": 0.0403, + "phase_currents": { + "A": 21.0, + "B": 21.0, + "C": -42.0 + } + }, + { + "t": 0.0417, + "phase_currents": { + "A": 0.0, + "B": 36.3731, + "C": -36.3731 + } + }, + { + "t": 0.0431, + "phase_currents": { + "A": -21.0, + "B": 42.0, + "C": -21.0 + } + }, + { + "t": 0.0444, + "phase_currents": { + "A": -36.3731, + "B": 36.3731, + "C": 0.0 + } + }, + { + "t": 0.0458, + "phase_currents": { + "A": -42.0, + "B": 21.0, + "C": 21.0 + } + }, + { + "t": 0.0472, + "phase_currents": { + "A": -36.3731, + "B": 0.0, + "C": 36.3731 + } + }, + { + "t": 0.0486, + "phase_currents": { + "A": -21.0, + "B": -21.0, + "C": 42.0 + } + } + ], + "outputs": [ + { + "type": "field_map", + "id": "induction_motor_field", + "path": "outputs/induction_motor_field.csv" + }, + { + "type": "vtk_field_series", + "id": "induction_motor_series", + "dir": "outputs", + "basename": "induction_motor_frame", + "quantities": [ + "B", + "H" + ] + }, + { + "type": "polyline_outlines", + "id": "induction_motor_outlines", + "path": "outputs/induction_motor_outlines.vtp" + }, + { + "type": "bore_avg_B", + "id": "induction_motor_bore", + "path": "outputs/induction_motor_bore.csv", + "vertices": [ + [ + 0.026, + 0.0 + ], + [ + 0.0251, + 0.0067 + ], + [ + 0.0225, + 0.013 + ], + [ + 0.0184, + 0.0184 + ], + [ + 0.013, + 0.0225 + ], + [ + 0.0067, + 0.0251 + ], + [ + 0.0, + 0.026 + ], + [ + -0.0067, + 0.0251 + ], + [ + -0.013, + 0.0225 + ], + [ + -0.0184, + 0.0184 + ], + [ + -0.0225, + 0.013 + ], + [ + -0.0251, + 0.0067 + ], + [ + -0.026, + 0.0 + ], + [ + -0.0251, + -0.0067 + ], + [ + -0.0225, + -0.013 + ], + [ + -0.0184, + -0.0184 + ], + [ + -0.013, + -0.0225 + ], + [ + -0.0067, + -0.0251 + ], + [ + 0.0, + -0.026 + ], + [ + 0.0067, + -0.0251 + ], + [ + 0.013, + -0.0225 + ], + [ + 0.0184, + -0.0184 + ], + [ + 0.0225, + -0.013 + ], + [ + 0.0251, + -0.0067 + ] + ] + }, + { + "type": "probe", + "id": "induction_motor_torque", + "probe_type": "torque", + "method": "stress_tensor", + "loop": { + "type": "polygon", + "vertices": [ + [ + 0.0262, + 0.0 + ], + [ + 0.0256, + 0.0054 + ], + [ + 0.0239, + 0.0107 + ], + [ + 0.0212, + 0.0154 + ], + [ + 0.0175, + 0.0195 + ], + [ + 0.0131, + 0.0227 + ], + [ + 0.0081, + 0.0249 + ], + [ + 0.0027, + 0.0261 + ], + [ + -0.0027, + 0.0261 + ], + [ + -0.0081, + 0.0249 + ], + [ + -0.0131, + 0.0227 + ], + [ + -0.0175, + 0.0195 + ], + [ + -0.0212, + 0.0154 + ], + [ + -0.0239, + 0.0107 + ], + [ + -0.0256, + 0.0054 + ], + [ + -0.0262, + 0.0 + ], + [ + -0.0256, + -0.0054 + ], + [ + -0.0239, + -0.0107 + ], + [ + -0.0212, + -0.0154 + ], + [ + -0.0175, + -0.0195 + ], + [ + -0.0131, + -0.0227 + ], + [ + -0.0081, + -0.0249 + ], + [ + -0.0027, + -0.0261 + ], + [ + 0.0027, + -0.0261 + ], + [ + 0.0081, + -0.0249 + ], + [ + 0.0131, + -0.0227 + ], + [ + 0.0175, + -0.0195 + ], + [ + 0.0212, + -0.0154 + ], + [ + 0.0239, + -0.0107 + ], + [ + 0.0256, + -0.0054 + ] + ] + }, + "path": "outputs/induction_motor_torque.csv" + }, + { + "type": "back_emf_probe", + "id": "induction_motor_phase_a_emf", + "component": "Bmag", + "region": { + "type": "polygon", + "vertices": [ + [ + 0.0395, + -0.0063 + ], + [ + 0.0494, + -0.0078 + ], + [ + 0.0494, + 0.0078 + ], + [ + 0.0395, + 0.0063 + ] + ] + }, + "path": "outputs/induction_motor_phase_a_emf.csv" + }, + { + "type": "back_emf_probe", + "id": "induction_motor_phase_b_emf", + "component": "Bmag", + "region": { + "type": "polygon", + "vertices": [ + [ + -0.0252, + -0.0311 + ], + [ + -0.0315, + -0.0389 + ], + [ + -0.0179, + -0.0467 + ], + [ + -0.0143, + -0.0373 + ] + ] + }, + "path": "outputs/induction_motor_phase_b_emf.csv" + }, + { + "type": "back_emf_probe", + "id": "induction_motor_phase_c_emf", + "component": "Bmag", + "region": { + "type": "polygon", + "vertices": [ + [ + -0.0143, + 0.0373 + ], + [ + -0.0179, + 0.0467 + ], + [ + -0.0315, + 0.0389 + ], + [ + -0.0252, + 0.0311 + ] + ] + }, + "path": "outputs/induction_motor_phase_c_emf.csv" + }, + { + "type": "mechanical_trace", + "id": "induction_motor_mechanical", + "path": "outputs/induction_motor_mechanical.csv", + "rotors": [ + "induction_rotor" + ] + } + ] +} diff --git a/mkdocs.yml b/mkdocs.yml index e6a3340..68a5a8a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,6 +85,7 @@ nav: - User Guide: - user-guide/running-simulations.md - Web GUI (Flask): user-guide/gui_flask.md + - Induction motor GUI workflow: user-guide/gui-workflows/induction-demo.md - Machines: - user-guide/machines/three-phase-stator.md - user-guide/machines/induction.md diff --git a/python/gui/AGENTS.md b/python/gui/AGENTS.md index 5738e6d..be8d88e 100644 --- a/python/gui/AGENTS.md +++ b/python/gui/AGENTS.md @@ -7,8 +7,31 @@ - Reuse the existing `SimulationManager` helper when wiring new routes. Ensure updates remain thread-safe and continue to enforce the single-simulation constraint. +- Stage 1 of the GUI overhaul introduced geometry previews, live field-map + visualisation, the DXF workspace, and the `/visualization.png` customisation + endpoint. The UI now includes a CAD-style top bar, workflow sidebar, and a + project-aware workspace summary. Keep the File-menu actions + (`/project/reset`, `/project/scenario`, `/project/export_dxf`) and + per-session project state intact when iterating on features. +- The results panel now exposes playback controls powered by SSE frame events. + Preserve the `sequence_index`, `frame_index`, and `frames` metadata so the + animation gallery can stay in sync with the solver log, and extend the JS + helpers (`registerFrame`, `advanceFrame`) when adding new visual streams. +- DXF handling lives in `python/gui/dxf_utils.py`; reuse its helpers when + parsing uploads, rendering previews, or exporting geometry. Layer selections + are stored in `PROJECTS[pid]['dxf_files']`—mutations should be mirrored in + tests to preserve coverage. +- Sample DXFs for the induction workflow live under + `docs/assets/dxf/induction_demo/`; keep them aligned with + `inputs/induction_gui_demo.json` and update the workflow doc when you add new + layers or assets. +- Follow-up work should expand tests when touching related code—fixtures clear + `PROJECTS`, so new project-oriented behaviour needs explicit coverage. - When modifying behaviour, update the user guide (`docs/user-guide/gui_flask.md`) and extend `tests/test_gui_flask.py` so new code paths stay covered. - Runtime artefacts such as uploads and generated logs should stay out of the repository. Use or extend the ignore rules in this directory if new folders are introduced. +- The helper scripts `scripts/setup_gui_env.sh` and + `scripts/maintain_gui_env.sh` keep local environments reproducible—run them + before hacking on the GUI or when tidying accumulated artefacts. diff --git a/python/gui/app_flask.py b/python/gui/app_flask.py index 91b532e..6cae78d 100644 --- a/python/gui/app_flask.py +++ b/python/gui/app_flask.py @@ -4,11 +4,15 @@ import json import queue +import re +import shutil import subprocess import threading import time +import uuid +import zipfile from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Set, Tuple from flask import ( Flask, @@ -17,17 +21,47 @@ redirect, render_template, request, - url_for, send_file, + session, + url_for, ) from werkzeug.utils import secure_filename +from python.gui.dxf_utils import ( + DxfError, + export_scenario_to_dxf, + load_primitives, + render_preview, + summarise_dxf, +) +from python.gui.render import ( + DEFAULT_LOG_FLOOR, + render_field_map_image, + render_geometry_preview, +) + ALLOWED_EXTENSIONS = {".json", ".dxf"} DEFAULT_SOLVER = "cg" DEFAULT_TOLERANCE = 1e-6 DEFAULT_MAX_ITERS = 20000 MAX_UPLOAD_SIZE_MB = 50 +DEFAULT_VECTOR_MODE = "linear" +DEFAULT_COLOR_SCALE = "linear" +DEFAULT_QUIVER_SKIP = 4 +DEFAULT_STREAMLINES = False +DXF_CATEGORY_OPTIONS = [ + ("domain", "Domain boundary"), + ("material", "Material region"), + ("magnet", "Magnet region"), + ("wire", "Conductor path"), + ("structural", "Structural / reference"), + ("unassigned", "Unassigned"), +] +DXF_DEFAULT_CATEGORY = "unassigned" +FIELD_MAP_REGEX = re.compile( + r"Frame\\s+(?P\\d+):\\s+wrote\\s+field_map\\s+'(?P[^']+)'\\s+to\\s+(?P.+)" +) class SimulationManager: @@ -42,6 +76,7 @@ def __init__(self, flask_app: Flask) -> None: self._running = False self._stop_requested = False self._metadata: Dict[str, Any] = {} + self._last_result: Dict[str, Any] = {} @property def is_running(self) -> bool: @@ -51,6 +86,10 @@ def is_running(self) -> bool: def queue(self) -> "queue.Queue[Dict[str, Any]]": return self._queue + def get_last_result(self) -> Dict[str, Any]: + with self._lock: + return dict(self._last_result) + def start( self, command: List[str], @@ -58,6 +97,7 @@ def start( scenario_path: Path, log_path: Path, extra_downloads: Optional[List[Dict[str, Any]]] = None, + field_outputs: Optional[List[Dict[str, Any]]] = None, ) -> None: with self._lock: if self._running: @@ -68,7 +108,12 @@ def start( self._metadata = { "scenario_path": scenario_path, "log_path": log_path, - "extra_downloads": extra_downloads or [], + "extra_downloads": list(extra_downloads or []), + "field_outputs": list(field_outputs or []), + "processed_field_maps": set(), + "process_cwd": Path.cwd(), + "rendered_frames": [], + "frame_sequence": 0, } self._queue.put({ "started": True, @@ -104,6 +149,13 @@ def reset(self) -> None: self._running = False self._stop_requested = False self._metadata = {} + self._last_result = {} + + def clear_last_result(self) -> None: + """Discard the cached visualisation payload.""" + + with self._lock: + self._last_result = {} def _drain_queue_locked(self) -> None: while True: @@ -112,6 +164,137 @@ def _drain_queue_locked(self) -> None: except queue.Empty: break + def _handle_field_map_event(self, *, frame: Optional[int], field_id: str, path_str: str) -> None: + scenario_path: Optional[Path] = self._metadata.get("scenario_path") + if scenario_path is None or not scenario_path.exists(): + return + + cleaned_path = path_str.strip().strip('"').strip("'") + if not cleaned_path: + return + + process_cwd: Optional[Path] = self._metadata.get("process_cwd") + scenario_dir = scenario_path.parent if scenario_path else None + output_path = _resolve_output_path( + Path(cleaned_path), + scenario_dir=scenario_dir, + process_cwd=process_cwd, + ) + + deadline = time.time() + 1.0 + while not output_path.exists() and time.time() < deadline: + time.sleep(0.05) + + sequence_index: Optional[int] = None + + with self._lock: + processed: Set[Path] = self._metadata.setdefault("processed_field_maps", set()) + if output_path in processed: + return + + field_outputs_meta: List[Dict[str, Any]] = self._metadata.setdefault("field_outputs", []) + if not any( + entry.get("path") and Path(entry.get("path")) == output_path for entry in field_outputs_meta + ): + field_outputs_meta.append({"id": field_id, "path": str(output_path)}) + + extra_downloads_meta: List[Dict[str, Any]] = self._metadata.setdefault("extra_downloads", []) + if not any( + item.get("path") and Path(item.get("path")) == output_path for item in extra_downloads_meta + ): + label = f"Field map CSV ({field_id})" if field_id else output_path.name + extra_downloads_meta.append( + { + "category": "result", + "path": str(output_path), + "label": label, + } + ) + + if not output_path.exists(): + return + + results_dir = Path(self._app.config["RESULTS_FOLDER"]) + timestamp = time.strftime("%Y%m%d-%H%M%S") + frame_suffix = f"_frame{frame}" if frame is not None else "" + filename = f"field_progress_{timestamp}{frame_suffix}.png" + image_path = results_dir / filename + + try: + render_field_map_image( + scenario_path, + output_path, + output_path=image_path, + vector_mode=DEFAULT_VECTOR_MODE, + quiver_skip=DEFAULT_QUIVER_SKIP, + color_scale=DEFAULT_COLOR_SCALE, + draw_boundaries=True, + streamlines=DEFAULT_STREAMLINES, + ) + except Exception as exc: # pragma: no cover - defensive guard + self._queue.put({ + "message": f"Failed to render field map '{field_id}': {exc}", + "warning": True, + }) + return + + caption = f"Frame {frame} field map" if frame is not None else f"Field map '{field_id}'" + + with self._lock: + processed = self._metadata.setdefault("processed_field_maps", set()) + processed.add(output_path) + self._metadata["latest_field_map"] = output_path + self._metadata["latest_image"] = image_path + sequence_index = int(self._metadata.get("frame_sequence", 0)) + self._metadata["frame_sequence"] = sequence_index + 1 + frames: List[Dict[str, Any]] = self._metadata.setdefault("rendered_frames", []) + frames.append( + { + "image": str(image_path), + "caption": caption, + "frame_index": frame, + "field_id": field_id, + "sequence_index": sequence_index, + } + ) + + self._queue.put( + { + "visualization": { + "image": f"/result-image/{image_path.name}", + "caption": caption, + "frame_index": frame, + "field_id": field_id, + "sequence_index": sequence_index, + } + } + ) + + def _candidate_field_maps(self) -> List[Path]: + scenario_path: Optional[Path] = self._metadata.get("scenario_path") + scenario_dir = scenario_path.parent if scenario_path else None + process_cwd: Optional[Path] = self._metadata.get("process_cwd") + candidates: List[Path] = [] + seen: Set[Path] = set() + latest = self._metadata.get("latest_field_map") + if isinstance(latest, Path): + candidates.append(latest) + seen.add(latest) + for entry in self._metadata.get("field_outputs", []): + path_value = entry.get("path") + if not path_value: + continue + path = _resolve_output_path( + Path(path_value), + scenario_dir=scenario_dir, + process_cwd=process_cwd, + ) + if path in seen: + continue + candidates.append(path) + seen.add(path) + return candidates + def _run_process(self, command: List[str]) -> None: log_path: Path = self._metadata["log_path"] log_path.parent.mkdir(parents=True, exist_ok=True) @@ -155,6 +338,18 @@ def _run_process(self, command: List[str]) -> None: event["progress"] = progress self._queue.put(event) + match = FIELD_MAP_REGEX.match(line) + if match: + frame_value = match.group("frame") + frame = int(frame_value) if frame_value is not None else None + field_id = match.group("id") + path_str = match.group("path") + self._handle_field_map_event( + frame=frame, + field_id=field_id, + path_str=path_str, + ) + return_code = process.wait() success = return_code == 0 and not self._stop_requested if self._stop_requested and return_code != 0: @@ -204,6 +399,98 @@ def _emit_completion(self, *, success: bool, message: str, scenario_path: Path) } ) + visualization_payload: Optional[Dict[str, Any]] = None + if success: + for candidate in self._candidate_field_maps(): + if not candidate.exists(): + continue + scenario_path_resolved = scenario_path if scenario_path.exists() else None + results_dir = Path(self._app.config["RESULTS_FOLDER"]) + timestamp = time.strftime("%Y%m%d-%H%M%S") + image_path = results_dir / f"field_result_{timestamp}.png" + try: + render_field_map_image( + scenario_path_resolved or scenario_path, + candidate, + output_path=image_path, + vector_mode=DEFAULT_VECTOR_MODE, + quiver_skip=DEFAULT_QUIVER_SKIP, + color_scale=DEFAULT_COLOR_SCALE, + draw_boundaries=True, + streamlines=DEFAULT_STREAMLINES, + ) + except Exception as exc: # pragma: no cover - defensive guard + self._queue.put({ + "message": f"Failed to render final field map: {exc}", + "warning": True, + }) + break + + downloads.append( + { + "category": "result", + "filename": image_path.name, + "label": "Field map image", + } + ) + frame_entry = None + with self._lock: + processed = self._metadata.setdefault("processed_field_maps", set()) + processed.add(candidate) + field_outputs_meta: List[Dict[str, Any]] = self._metadata.setdefault("field_outputs", []) + field_id = None + for entry in field_outputs_meta: + path_value = entry.get("path") + if path_value and Path(path_value) == candidate: + field_id = entry.get("id") + break + sequence_index = int(self._metadata.get("frame_sequence", 0)) + self._metadata["frame_sequence"] = sequence_index + 1 + frames: List[Dict[str, Any]] = self._metadata.setdefault("rendered_frames", []) + frame_entry = { + "image": str(image_path), + "caption": "Final field map", + "frame_index": None, + "field_id": field_id, + "sequence_index": sequence_index, + } + frames.append(frame_entry) + self._metadata["latest_field_map"] = candidate + self._metadata["latest_image"] = image_path + frames_copy = list(frames) + self._last_result = { + "scenario_path": str(scenario_path), + "field_map": str(candidate), + "image": str(image_path), + "caption": "Final field map", + "settings": { + "vector_mode": DEFAULT_VECTOR_MODE, + "color_scale": DEFAULT_COLOR_SCALE, + "quiver_skip": DEFAULT_QUIVER_SKIP, + "draw_boundaries": True, + "streamlines": DEFAULT_STREAMLINES, + }, + "frames": frames_copy, + } + + visualization_payload = { + "image": f"/result-image/{image_path.name}", + "caption": "Final field map", + "frame_index": frame_entry.get("frame_index") if frame_entry else None, + "field_id": frame_entry.get("field_id") if frame_entry else None, + "sequence_index": frame_entry.get("sequence_index") if frame_entry else None, + } + break + else: + with self._lock: + frames_copy = list(self._metadata.get("rendered_frames", [])) + self._last_result = {"message": "No field map output generated.", "frames": frames_copy} + visualization_payload = {"message": "No field map output generated."} + else: + with self._lock: + frames_copy = list(self._metadata.get("rendered_frames", [])) + self._last_result = {"frames": frames_copy} + event: Dict[str, Any] = {"complete": True, "message": message, "success": success} if success: event["progress"] = 100 @@ -211,6 +498,8 @@ def _emit_completion(self, *, success: bool, message: str, scenario_path: Path) event["stopped"] = True if downloads: event["downloads"] = downloads + if visualization_payload: + event["visualization"] = visualization_payload self._queue.put(event) self._stop_requested = False @@ -244,72 +533,704 @@ def _extract_progress(line: str) -> Optional[int]: Path(app.config["UPLOAD_FOLDER"]).mkdir(parents=True, exist_ok=True) Path(app.config["RESULTS_FOLDER"]).mkdir(parents=True, exist_ok=True) +app.secret_key = "mag-sim-gui" + manager = SimulationManager(app) +PROJECTS: Dict[str, Dict[str, Any]] = {} + def allowed_file(filename: str) -> bool: return Path(filename).suffix.lower() in ALLOWED_EXTENSIONS +def _build_form_state(overrides: Optional[Dict[str, str]] = None) -> Dict[str, str]: + state = { + "solver": DEFAULT_SOLVER, + "tol": f"{DEFAULT_TOLERANCE}", + "max_iters": f"{DEFAULT_MAX_ITERS}", + "outputs": "", + } + if overrides: + for key, value in overrides.items(): + if key in state and value is not None: + state[key] = value + return state + + +def _register_project( + scenario_path: Path, spec: Dict[str, Any], original_name: str +) -> Tuple[str, Dict[str, Any]]: + project_id = uuid.uuid4().hex + PROJECTS[project_id] = { + "scenario_path": scenario_path, + "spec": spec, + "original_filename": original_name, + "scenario_name": spec.get("name"), + "created_label": time.strftime("%Y-%m-%d %H:%M:%S"), + "preview_path": None, + "preview_notice": None, + "preview_version": None, + "form_state": _build_form_state(), + "dxf_files": {}, + "dxf_preview_path": None, + "dxf_preview_version": None, + "dxf_notice": None, + } + return project_id, PROJECTS[project_id] + + +def _register_blank_project() -> Tuple[str, Dict[str, Any]]: + project_id = uuid.uuid4().hex + PROJECTS[project_id] = { + "scenario_path": None, + "spec": {}, + "original_filename": None, + "scenario_name": None, + "created_label": time.strftime("%Y-%m-%d %H:%M:%S"), + "preview_path": None, + "preview_notice": None, + "preview_version": None, + "form_state": _build_form_state(), + "dxf_files": {}, + "dxf_preview_path": None, + "dxf_preview_version": None, + "dxf_notice": None, + } + return project_id, PROJECTS[project_id] + + +def _ensure_project_for_dxf(project_id: Optional[str]) -> Tuple[str, Dict[str, Any]]: + active_id, project = _current_project(project_id) + if project is None: + active_id, project = _register_blank_project() + session["project_id"] = active_id + return active_id, project + + +def _update_dxf_preview(project_id: str) -> None: + project = PROJECTS.get(project_id) + if not project: + return + + preview_path = project.get("dxf_preview_path") + if isinstance(preview_path, Path): + preview_path.unlink(missing_ok=True) + + primitives = [] + dxf_store = project.get("dxf_files", {}) + if isinstance(dxf_store, dict): + for entry in dxf_store.values(): + if not isinstance(entry, dict): + continue + path = entry.get("path") + if not isinstance(path, Path) or not path.exists(): + continue + layers = entry.get("layers", []) + if not isinstance(layers, list): + continue + selected_layers = [layer.get("name") for layer in layers if layer.get("selected")] + selected_layers = [name for name in selected_layers if isinstance(name, str) and name] + if not selected_layers: + continue + categories = { + layer.get("name"): layer.get("category") or DXF_DEFAULT_CATEGORY + for layer in layers + if isinstance(layer, dict) and layer.get("name") + } + try: + primitives.extend(load_primitives(path, selected_layers, categories)) + except DxfError as exc: + project["dxf_notice"] = str(exc) + return + + if not primitives: + project["dxf_preview_path"] = None + project["dxf_preview_version"] = None + project["dxf_notice"] = "Select DXF layers to preview." + return + + output_name = f"dxf_preview_{project_id}_{time.strftime('%Y%m%d-%H%M%S')}" + preview_destination = Path(app.config["RESULTS_FOLDER"]) / f"{output_name}.png" + try: + render_preview(primitives, preview_destination) + except DxfError as exc: + project["dxf_notice"] = str(exc) + preview_destination.unlink(missing_ok=True) + return + + project["dxf_preview_path"] = preview_destination + project["dxf_preview_version"] = time.time() + project["dxf_notice"] = None + + +def _discard_project(project_id: str) -> None: + project = PROJECTS.pop(project_id, None) + if not project: + return + + preview_path = project.get("preview_path") + if isinstance(preview_path, Path): + preview_path.unlink(missing_ok=True) + + dxf_preview_path = project.get("dxf_preview_path") + if isinstance(dxf_preview_path, Path): + dxf_preview_path.unlink(missing_ok=True) + + scenario_path = project.get("scenario_path") + if isinstance(scenario_path, Path): + scenario_path.unlink(missing_ok=True) + + dxf_files = project.get("dxf_files", {}) + if isinstance(dxf_files, dict): + for entry in dxf_files.values(): + path = entry.get("path") + if isinstance(path, Path): + path.unlink(missing_ok=True) + + +def _current_project( + explicit_id: Optional[str] = None, +) -> Tuple[Optional[str], Optional[Dict[str, Any]]]: + project_id = explicit_id or session.get("project_id") + if not project_id: + return None, None + + project = PROJECTS.get(project_id) + if project is None: + if explicit_id is None: + session.pop("project_id", None) + return None, None + + return project_id, project + + +def _index_context( + *, + error: Optional[str] = None, + preview_url: Optional[str] = None, + preview_error: Optional[str] = None, + preview_notice: Optional[str] = None, + form_state: Optional[Dict[str, str]] = None, + project_id: Optional[str] = None, + status_message: Optional[str] = None, +) -> Dict[str, Any]: + active_id, project = _current_project(project_id) + + if form_state is None: + if project and project.get("form_state"): + form_state = dict(project["form_state"]) + else: + form_state = _build_form_state() + else: + form_state = _build_form_state(form_state) + + preview_source = preview_url + preview_token = None + notice = preview_notice + if project: + stored_preview = project.get("preview_path") + if stored_preview and Path(stored_preview).exists(): + if not preview_source: + preview_source = url_for("result_image", filename=Path(stored_preview).name) + preview_token = project.get("preview_version") + if not notice and project.get("preview_notice"): + notice = project["preview_notice"] + + last_result = manager.get_last_result() + last_image_url = None + visualization_caption = None + visualization_message = last_result.get("message") if last_result else None + visualization_defaults = { + "vector_mode": DEFAULT_VECTOR_MODE, + "color_scale": DEFAULT_COLOR_SCALE, + "quiver_skip": DEFAULT_QUIVER_SKIP, + "draw_boundaries": True, + "streamlines": DEFAULT_STREAMLINES, + } + frame_history: List[Dict[str, Any]] = [] + if last_result: + image_path = Path(last_result.get("image", "")) + if image_path.exists(): + last_image_url = url_for("result_image", filename=image_path.name) + visualization_caption = last_result.get("caption") + visualization_message = None + settings = last_result.get("settings", {}) + for key in visualization_defaults: + if key in settings: + visualization_defaults[key] = settings[key] + frames_value = last_result.get("frames") + if isinstance(frames_value, list): + for entry in frames_value: + if not isinstance(entry, dict): + continue + image_value = entry.get("image") + image_url = None + if image_value: + try: + image_path_value = Path(image_value) + except (TypeError, ValueError): # pragma: no cover - defensive guard + image_path_value = None + if image_path_value and image_path_value.exists(): + image_url = url_for("result_image", filename=image_path_value.name) + elif isinstance(image_value, str): + image_url = image_value + if not image_url: + continue + frame_history.append( + { + "image": image_url, + "caption": entry.get("caption"), + "frame_index": entry.get("frame_index"), + "field_id": entry.get("field_id"), + "sequence_index": entry.get("sequence_index"), + } + ) + frame_history.sort(key=lambda item: (item.get("sequence_index") or 0)) + + project_summary = None + project_has_scenario = False + dxf_files_summary: List[Dict[str, Any]] = [] + dxf_preview_url = None + dxf_preview_token = None + dxf_notice = None + has_dxf_selection = False + + if project and active_id: + scenario_path_value = project.get("scenario_path") + scenario_exists = isinstance(scenario_path_value, Path) and scenario_path_value.exists() + project_has_scenario = scenario_exists + project_summary = { + "id": active_id, + "filename": project.get("original_filename"), + "scenario_name": project.get("scenario_name"), + "created_label": project.get("created_label"), + "has_scenario": scenario_exists, + } + + dxf_store = project.get("dxf_files", {}) + if isinstance(dxf_store, dict): + for dxf_id, entry in dxf_store.items(): + if not isinstance(entry, dict): + continue + layers_payload: List[Dict[str, Any]] = [] + for layer in entry.get("layers", []): + if not isinstance(layer, dict): + continue + category_value = layer.get("category") or DXF_DEFAULT_CATEGORY + selected_value = bool(layer.get("selected", False)) + layers_payload.append( + { + "form_id": layer.get("form_id"), + "name": layer.get("name"), + "entity_count": layer.get("entity_count", 0), + "types": layer.get("types", []), + "selected": selected_value, + "category": category_value, + } + ) + has_dxf_selection = has_dxf_selection or selected_value + layers_payload.sort(key=lambda item: (item["name"] or "").lower()) + entry_path = entry.get("path") + display_name = entry.get("original_name") or entry.get("name") + if not display_name and isinstance(entry_path, Path): + display_name = entry_path.name + dxf_files_summary.append( + { + "id": dxf_id, + "name": display_name or dxf_id, + "layers": layers_payload, + "uploaded_label": entry.get("uploaded_label"), + } + ) + dxf_files_summary.sort(key=lambda item: item["name"].lower()) + + preview_path = project.get("dxf_preview_path") + if isinstance(preview_path, Path) and preview_path.exists(): + dxf_preview_url = url_for("result_image", filename=preview_path.name) + dxf_preview_token = project.get("dxf_preview_version") + dxf_notice = project.get("dxf_notice") + + return { + "running": manager.is_running, + "error": error, + "form_state": form_state, + "preview_url": preview_source, + "preview_error": preview_error, + "preview_notice": notice, + "preview_token": preview_token, + "visualization_defaults": visualization_defaults, + "last_visualization_url": last_image_url, + "last_visualization_caption": visualization_caption, + "visualization_message": visualization_message, + "visualization_visible": bool(last_result), + "last_visualization_frames": frame_history, + "visualization_has_frames": bool(frame_history), + "DEFAULT_LOG_FLOOR": DEFAULT_LOG_FLOOR, + "project": project_summary, + "has_project": project_summary is not None, + "project_download_url": url_for("project_scenario") if project_has_scenario else None, + "project_export_dxf_url": url_for("project_export_dxf") if project_has_scenario else None, + "project_has_scenario": project_has_scenario, + "form_requires_file": not project_has_scenario, + "status_message": status_message, + "dxf_files": dxf_files_summary, + "dxf_preview_url": dxf_preview_url, + "dxf_preview_token": dxf_preview_token, + "dxf_notice": dxf_notice, + "dxf_categories": DXF_CATEGORY_OPTIONS, + "has_dxf_selection": has_dxf_selection, + } + + +def _store_scenario_file(geometry) -> Tuple[Path, dict, str, str]: + suffix = Path(geometry.filename).suffix.lower() + timestamp = time.strftime("%Y%m%d-%H%M%S") + original_name = geometry.filename or f"scenario_{timestamp}{suffix}" + safe_name = secure_filename(original_name) or f"scenario_{timestamp}{suffix}" + scenario_path = Path(app.config["UPLOAD_FOLDER"]) / f"{timestamp}_{safe_name}" + + scenario_path.parent.mkdir(parents=True, exist_ok=True) + geometry.save(scenario_path) + try: + spec = json.loads(scenario_path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + scenario_path.unlink(missing_ok=True) + raise ValueError("Uploaded JSON is invalid.") from exc + return scenario_path, spec, timestamp, original_name + + +def _resolve_output_path( + raw_path: Path, *, scenario_dir: Optional[Path], process_cwd: Optional[Path] +) -> Path: + if raw_path.is_absolute(): + return raw_path + + candidates: List[Path] = [] + if scenario_dir is not None: + candidates.append(scenario_dir / raw_path) + if process_cwd is not None: + candidates.append(process_cwd / raw_path) + + for candidate in candidates: + if candidate.exists(): + return candidate + + if candidates: + return candidates[0] + + return raw_path + + +def _collect_field_outputs(spec: dict, scenario_dir: Path) -> List[Dict[str, Any]]: + outputs: List[Dict[str, Any]] = [] + for entry in spec.get("outputs", []): + if entry.get("type") != "field_map": + continue + identifier = entry.get("id") + path_value = entry.get("path") + if not path_value and identifier: + path_value = f"outputs/{identifier}.csv" + if not path_value: + continue + raw_path = Path(path_value) + resolved = _resolve_output_path( + raw_path, scenario_dir=scenario_dir, process_cwd=Path.cwd() + ) + outputs.append({"id": identifier, "path": resolved}) + return outputs + + +def _collect_cli_field_outputs(outputs_arg: str) -> List[Dict[str, Any]]: + outputs: List[Dict[str, Any]] = [] + if not outputs_arg: + return outputs + + tokens = [token.strip() for token in re.split(r"[,\s]+", outputs_arg) if token.strip()] + for token in tokens: + if token.lower() in {"all", "none"}: + continue + identifier = token + path_override = None + if "=" in token: + identifier, path_override = token.split("=", 1) + elif ":" in token: + identifier, path_override = token.split(":", 1) + identifier = identifier.strip() + if not identifier: + continue + raw_path = Path(path_override.strip()) if path_override else Path("outputs") / f"{identifier}.csv" + outputs.append({"id": identifier, "path": raw_path}) + + return outputs + + +@app.post("/dxf/upload") +def upload_dxf_files() -> Response: + project_hint = request.form.get("project_id") + project_id, project = _ensure_project_for_dxf(project_hint) + + files = request.files.getlist("dxf_files") + if not files or all(not f.filename for f in files): + return redirect(url_for("index", error="Select at least one DXF file to import.")) + + errors: List[str] = [] + imported = 0 + dxf_store = project.setdefault("dxf_files", {}) + + for storage in files: + if not storage.filename: + continue + suffix = Path(storage.filename).suffix.lower() + if suffix != ".dxf": + errors.append(f"Unsupported extension for {storage.filename}; only DXF files are allowed.") + continue + + timestamp = time.strftime("%Y%m%d-%H%M%S") + safe_name = secure_filename(storage.filename) or f"drawing_{timestamp}.dxf" + destination = Path(app.config["UPLOAD_FOLDER"]) / f"{project_id}_{timestamp}_{safe_name}" + destination.parent.mkdir(parents=True, exist_ok=True) + storage.save(destination) + + try: + layer_summary = summarise_dxf(destination) + except DxfError as exc: + destination.unlink(missing_ok=True) + errors.append(str(exc)) + continue + + if not layer_summary: + destination.unlink(missing_ok=True) + errors.append(f"{storage.filename} does not contain supported geometry.") + continue + + entry_id = uuid.uuid4().hex + dxf_store[entry_id] = { + "id": entry_id, + "path": destination, + "original_name": storage.filename, + "uploaded_label": time.strftime("%Y-%m-%d %H:%M:%S"), + "layers": [ + { + "name": info.name, + "entity_count": info.entity_count, + "types": list(info.types), + "selected": True, + "category": DXF_DEFAULT_CATEGORY, + "form_id": uuid.uuid4().hex[:8], + } + for info in layer_summary + ], + } + imported += 1 + + notice = None + if imported: + _update_dxf_preview(project_id) + project["dxf_notice"] = None + notice = f"Imported {imported} DXF file{'s' if imported != 1 else ''}." + + if errors: + project["dxf_notice"] = errors[0] + if not imported: + return redirect(url_for("index", error=errors[0])) + if notice: + return redirect(url_for("index", notice=f"{notice} Some files could not be processed.")) + return redirect(url_for("index", notice="Some DXF files could not be processed.")) + + if notice: + return redirect(url_for("index", notice=notice)) + + return redirect(url_for("index")) + + +@app.post("/dxf//layers") +def configure_dxf_layers(dxf_id: str) -> Response: + project_hint = request.form.get("project_id") + active_id, project = _current_project(project_hint) + if not project or not active_id: + return redirect(url_for("index", error="Project not found.")) + + session["project_id"] = active_id + dxf_store = project.get("dxf_files", {}) + entry = dxf_store.get(dxf_id) if isinstance(dxf_store, dict) else None + if entry is None: + return redirect(url_for("index", error="DXF entry not found.")) + + valid_categories = {choice[0] for choice in DXF_CATEGORY_OPTIONS} + layers = entry.get("layers", []) + if isinstance(layers, list): + for layer in layers: + if not isinstance(layer, dict): + continue + form_id = layer.get("form_id") + if not form_id: + continue + selected = request.form.get(f"layer-{form_id}-selected") == "on" + category = request.form.get(f"layer-{form_id}-category") or DXF_DEFAULT_CATEGORY + if category not in valid_categories: + category = DXF_DEFAULT_CATEGORY + layer["selected"] = selected + layer["category"] = category + + project["dxf_notice"] = None + _update_dxf_preview(active_id) + + return redirect(url_for("index", notice="DXF layers updated.")) + + +@app.post("/dxf//delete") +def delete_dxf_entry(dxf_id: str) -> Response: + project_hint = request.form.get("project_id") + active_id, project = _current_project(project_hint) + if not project or not active_id: + return redirect(url_for("index", error="Project not found.")) + + session["project_id"] = active_id + dxf_store = project.get("dxf_files", {}) + entry = None + if isinstance(dxf_store, dict): + entry = dxf_store.pop(dxf_id, None) + + if isinstance(entry, dict): + path = entry.get("path") + if isinstance(path, Path): + path.unlink(missing_ok=True) + project["dxf_notice"] = None + _update_dxf_preview(active_id) + return redirect(url_for("index", notice="Removed DXF file.")) + + _update_dxf_preview(active_id) + return redirect(url_for("index", error="DXF entry not found.")) + + @app.get("/") def index() -> str: error = request.args.get("error") + notice = request.args.get("notice") return render_template( "index.html", - running=manager.is_running, - error=error, - default_solver=DEFAULT_SOLVER, - default_tolerance=DEFAULT_TOLERANCE, - default_max_iters=DEFAULT_MAX_ITERS, + **_index_context(error=error, status_message=notice), ) @app.post("/upload") def upload() -> Response: + action = (request.form.get("action") or "run").lower() if manager.is_running: return redirect(url_for("index", error="A simulation is already running.")) geometry = request.files.get("geometry_file") - if geometry is None or geometry.filename == "": - return redirect(url_for("index", error="Select a scenario JSON before running.")) + project_hint = request.form.get("project_id") + current_project_id = session.get("project_id") - if not allowed_file(geometry.filename): - return redirect(url_for("index", error="Unsupported file type. Use JSON scenarios.")) + project_id: Optional[str] = None + project: Optional[Dict[str, Any]] = None + scenario_path: Optional[Path] = None + spec: Optional[Dict[str, Any]] = None - solver = (request.form.get("solver") or DEFAULT_SOLVER).lower() + if geometry is not None and geometry.filename: + if not allowed_file(geometry.filename): + return redirect(url_for("index", error="Unsupported file type. Use JSON scenarios.")) + + suffix = Path(geometry.filename).suffix.lower() + if suffix != ".json": + return redirect(url_for("index", error="Import DXF files via the CAD workspace.")) + + if current_project_id and current_project_id in PROJECTS: + _discard_project(current_project_id) + + try: + scenario_path, spec, _, original_name = _store_scenario_file(geometry) + except ValueError as exc: + return redirect(url_for("index", error=str(exc))) + + project_id, project = _register_project(scenario_path, spec, original_name) + session["project_id"] = project_id + else: + project_id, project = _current_project(project_hint) + if project is None: + return redirect(url_for("index", error="Select or upload a scenario before continuing.")) + + scenario_path = project.get("scenario_path") + spec = project.get("spec") + if not isinstance(scenario_path, Path) or not scenario_path.exists(): + return redirect(url_for("index", error="Scenario file is missing for this project.")) + if not isinstance(spec, dict): + return redirect(url_for("index", error="Scenario metadata is unavailable for this project.")) + + assert project_id is not None and project is not None + session["project_id"] = project_id + if scenario_path is None: + scenario_path = project["scenario_path"] + if spec is None: + spec = project["spec"] + + form_state = _build_form_state( + { + "solver": request.form.get("solver"), + "tol": request.form.get("tol"), + "max_iters": request.form.get("max_iters"), + "outputs": request.form.get("outputs"), + } + ) + project["form_state"] = dict(form_state) + project["spec"] = spec + project["scenario_path"] = scenario_path + project["scenario_name"] = spec.get("name") + + if action == "preview": + preview_path_existing = project.get("preview_path") + if isinstance(preview_path_existing, Path): + preview_path_existing.unlink(missing_ok=True) + + preview_filename = f"geometry_preview_{project_id}_{time.strftime('%Y%m%d-%H%M%S')}.png" + preview_path = Path(app.config["RESULTS_FOLDER"]) / preview_filename + try: + render_geometry_preview(scenario_path, preview_path) + except RuntimeError as exc: + project["preview_path"] = None + project["preview_version"] = None + project["preview_notice"] = None + return render_template( + "index.html", + **_index_context( + preview_error=str(exc), + form_state=form_state, + project_id=project_id, + ), + ) + + project["preview_path"] = preview_path + project["preview_notice"] = ( + f"Preview generated from {project.get('original_filename') or scenario_path.name}" + ) + project["preview_version"] = time.time() + + return render_template( + "index.html", + **_index_context( + preview_notice=project["preview_notice"], + form_state=form_state, + project_id=project_id, + ), + ) + + solver = (form_state["solver"] or DEFAULT_SOLVER).lower() if solver not in {"cg", "sor"}: return redirect(url_for("index", error="Solver must be either CG or SOR.")) - tol_raw = request.form.get("tol", str(DEFAULT_TOLERANCE)) try: - tolerance = float(tol_raw) + tolerance = float(form_state["tol"]) except ValueError: return redirect(url_for("index", error="Tolerance must be numeric.")) - max_iters_raw = request.form.get("max_iters", str(DEFAULT_MAX_ITERS)) try: - max_iters = int(max_iters_raw) + max_iters = int(form_state["max_iters"]) except ValueError: return redirect(url_for("index", error="Max iterations must be an integer.")) - outputs_value = (request.form.get("outputs") or "").strip() - - suffix = Path(geometry.filename).suffix.lower() - timestamp = time.strftime("%Y%m%d-%H%M%S") - safe_name = secure_filename(geometry.filename) or f"scenario_{timestamp}{suffix}" - scenario_path = Path(app.config["UPLOAD_FOLDER"]) / f"{timestamp}_{safe_name}" - - if suffix == ".json": - scenario_path.parent.mkdir(parents=True, exist_ok=True) - geometry.save(scenario_path) - try: - json.loads(scenario_path.read_text(encoding="utf-8")) - except json.JSONDecodeError: - scenario_path.unlink(missing_ok=True) - return redirect(url_for("index", error="Uploaded JSON is invalid.")) - else: - # DXF support is not yet implemented for the Flask prototype. - return redirect(url_for("index", error="DXF uploads are not supported yet.")) + outputs_value = form_state["outputs"].strip() command = [ "./build/motor_sim", @@ -327,13 +1248,38 @@ def upload() -> Response: if outputs_value: command.extend(["--outputs", outputs_value]) - log_path = Path(app.config["RESULTS_FOLDER"]) / f"simulation_{timestamp}.log" + run_stamp = time.strftime("%Y%m%d-%H%M%S") + log_path = Path(app.config["RESULTS_FOLDER"]) / f"simulation_{run_stamp}.log" + + field_outputs = _collect_field_outputs(spec, scenario_path.parent) + existing_paths: Set[Path] = {Path(entry["path"]) for entry in field_outputs} + if outputs_value: + for entry in _collect_cli_field_outputs(outputs_value): + resolved = _resolve_output_path( + Path(entry["path"]), + scenario_dir=scenario_path.parent, + process_cwd=Path.cwd(), + ) + if resolved not in existing_paths: + field_outputs.append({"id": entry.get("id"), "path": resolved}) + existing_paths.add(resolved) + + extra_downloads = [ + { + "category": "result", + "path": str(entry["path"]), + "label": f"Field map CSV ({entry['id']})" if entry.get("id") else Path(entry["path"]).name, + } + for entry in field_outputs + ] try: manager.start( command, scenario_path=scenario_path, log_path=log_path, + extra_downloads=extra_downloads, + field_outputs=[{"id": entry.get("id"), "path": str(entry["path"])} for entry in field_outputs], ) except RuntimeError as exc: return redirect(url_for("index", error=str(exc))) @@ -390,5 +1336,127 @@ def download() -> Response: return send_file(file_path, as_attachment=True) +@app.post("/project/reset") +def project_reset() -> Response: + if manager.is_running: + return redirect(url_for("index", error="Stop the running simulation before clearing the project.")) + + project_id = session.pop("project_id", None) + if project_id: + _discard_project(project_id) + + manager.clear_last_result() + return redirect(url_for("index", notice="Project cleared.")) + + +@app.get("/project/scenario") +def project_scenario() -> Response: + project_id, project = _current_project() + if project is None: + abort(404) + + scenario_path = project.get("scenario_path") + if not isinstance(scenario_path, Path) or not scenario_path.exists(): + abort(404) + + download_name = project.get("original_filename") or scenario_path.name + return send_file(scenario_path, as_attachment=True, download_name=download_name) + + +@app.get("/project/export_dxf") +def project_export_dxf() -> Response: + project_id, project = _current_project() + if project is None: + abort(404) + + scenario_path = project.get("scenario_path") + if not isinstance(scenario_path, Path) or not scenario_path.exists(): + return redirect(url_for("index", error="Load a scenario before exporting DXF geometry.")) + + export_root = Path(app.config["RESULTS_FOLDER"]) / f"dxf_export_{uuid.uuid4().hex}" + try: + exported_paths = export_scenario_to_dxf(scenario_path, export_root) + except DxfError as exc: + shutil.rmtree(export_root, ignore_errors=True) + return redirect(url_for("index", error=str(exc))) + + if not exported_paths: + shutil.rmtree(export_root, ignore_errors=True) + return redirect(url_for("index", error="Scenario does not contain exportable geometry.")) + + archive_path = export_root / f"{scenario_path.stem}_geometry.zip" + with zipfile.ZipFile(archive_path, "w") as archive: + for item in exported_paths: + archive.write(item, arcname=item.name) + + return send_file(archive_path, as_attachment=True, download_name=archive_path.name) + + +@app.get("/result-image/") +def result_image(filename: str) -> Response: + safe_name = secure_filename(filename) + if not safe_name: + abort(400) + + file_path = Path(app.config["RESULTS_FOLDER"]) / safe_name + if not file_path.exists(): + abort(404) + + return send_file(file_path, mimetype="image/png") + + +@app.get("/visualization.png") +def visualization_image() -> Response: + last_result = manager.get_last_result() + scenario_path = Path(last_result.get("scenario_path", "")) + field_map_path = Path(last_result.get("field_map", "")) + if not scenario_path.exists() or not field_map_path.exists(): + abort(404) + + vector_mode = request.args.get("vector", DEFAULT_VECTOR_MODE) + if vector_mode not in {"linear", "log", "off"}: + abort(400) + + try: + quiver_skip = int(request.args.get("skip", DEFAULT_QUIVER_SKIP)) + if quiver_skip < 1: + raise ValueError + except ValueError: + abort(400) + + color_scale = request.args.get("scale", DEFAULT_COLOR_SCALE) + if color_scale not in {"linear", "log"}: + abort(400) + + draw_boundaries = request.args.get("boundaries", "1") != "0" + streamlines = request.args.get("stream", "0") == "1" + + try: + log_floor_value = float(request.args.get("log_floor", DEFAULT_LOG_FLOOR)) + vector_floor_value = float(request.args.get("vector_floor", DEFAULT_LOG_FLOOR)) + except ValueError: + abort(400) + + data = render_field_map_image( + scenario_path, + field_map_path, + output_path=None, + vector_mode=vector_mode, + quiver_skip=quiver_skip, + color_scale=color_scale, + draw_boundaries=draw_boundaries, + streamlines=streamlines, + log_floor=log_floor_value, + vector_log_floor=vector_floor_value, + ) + + if isinstance(data, Path): # pragma: no cover - defensive guard + payload = data.read_bytes() + else: + payload = data + + return Response(payload, mimetype="image/png") + + if __name__ == "__main__": # pragma: no cover - manual execution helper app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/python/gui/dxf_utils.py b/python/gui/dxf_utils.py new file mode 100644 index 0000000..91c2d4a --- /dev/null +++ b/python/gui/dxf_utils.py @@ -0,0 +1,267 @@ +from __future__ import annotations + +import math +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Sequence, Tuple + +import ezdxf +import matplotlib + +matplotlib.use("Agg", force=True) + +import matplotlib.pyplot as plt # noqa: E402 +from matplotlib.patches import Circle # noqa: E402 + + +class DxfError(RuntimeError): + """Raised when DXF artefacts cannot be processed.""" + + +@dataclass +class DxfLayerInfo: + name: str + entity_count: int + types: Sequence[str] + + +@dataclass +class DxfPrimitive: + kind: str + points: Sequence[Tuple[float, float]] | None = None + center: Tuple[float, float] | None = None + radius: float | None = None + layer: str = "0" + category: Optional[str] = None + + +_CATEGORY_COLOURS = { + "domain": "#0d6efd", + "material": "#198754", + "magnet": "#d63384", + "wire": "#fd7e14", + "structural": "#20c997", + "unassigned": "#6c757d", +} + + +def summarise_dxf(path: Path) -> List[DxfLayerInfo]: + """Return basic layer statistics for the supplied DXF file.""" + + try: + doc = ezdxf.readfile(str(path)) + except (OSError, ezdxf.DXFStructureError, ezdxf.DXFTableEntryError) as exc: + raise DxfError(f"Failed to read DXF '{path.name}': {exc}") from exc + + counts: Dict[str, int] = {} + types: Dict[str, set[str]] = {} + + for entity in doc.modelspace(): + layer = entity.dxf.layer or "0" + counts[layer] = counts.get(layer, 0) + 1 + types.setdefault(layer, set()).add(entity.dxftype()) + + summary: List[DxfLayerInfo] = [] + for layer, count in sorted(counts.items()): + summary.append(DxfLayerInfo(name=layer, entity_count=count, types=sorted(types[layer]))) + + return summary + + +def _polyline_points(entity) -> List[Tuple[float, float]]: + if entity.dxftype() == "LWPOLYLINE": + return [(pt[0], pt[1]) for pt in entity.get_points("xy")] + if entity.dxftype() == "POLYLINE": + return [(v.dxf.location.x, v.dxf.location.y) for v in entity.vertices()] + if entity.dxftype() == "LINE": + return [ + (float(entity.dxf.start.x), float(entity.dxf.start.y)), + (float(entity.dxf.end.x), float(entity.dxf.end.y)), + ] + return [] + + +def load_primitives(path: Path, layers: Iterable[str], categories: Dict[str, Optional[str]]) -> List[DxfPrimitive]: + """Extract drawable primitives for the requested layers.""" + + selected = {layer.lower() for layer in layers} + if not selected: + return [] + + try: + doc = ezdxf.readfile(str(path)) + except (OSError, ezdxf.DXFStructureError, ezdxf.DXFTableEntryError) as exc: + raise DxfError(f"Failed to read DXF '{path.name}': {exc}") from exc + + primitives: List[DxfPrimitive] = [] + for entity in doc.modelspace(): + layer = (entity.dxf.layer or "0") + if layer.lower() not in selected: + continue + + category = categories.get(layer) + if entity.dxftype() in {"LWPOLYLINE", "POLYLINE", "LINE"}: + points = _polyline_points(entity) + if not points: + continue + primitives.append(DxfPrimitive(kind="polyline", points=points, layer=layer, category=category)) + elif entity.dxftype() == "CIRCLE": + center = (float(entity.dxf.center.x), float(entity.dxf.center.y)) + primitives.append( + DxfPrimitive( + kind="circle", + center=center, + radius=float(entity.dxf.radius), + layer=layer, + category=category, + ) + ) + + return primitives + + +def render_preview(primitives: Sequence[DxfPrimitive], output_path: Path) -> Path: + """Render DXF primitives into an image.""" + + if not primitives: + raise DxfError("No DXF layers selected for preview.") + + xmin = math.inf + xmax = -math.inf + ymin = math.inf + ymax = -math.inf + + for primitive in primitives: + if primitive.kind == "polyline" and primitive.points: + xs, ys = zip(*primitive.points) + xmin = min(xmin, min(xs)) + xmax = max(xmax, max(xs)) + ymin = min(ymin, min(ys)) + ymax = max(ymax, max(ys)) + elif primitive.kind == "circle" and primitive.center and primitive.radius is not None: + cx, cy = primitive.center + r = primitive.radius + xmin = min(xmin, cx - r) + xmax = max(xmax, cx + r) + ymin = min(ymin, cy - r) + ymax = max(ymax, cy + r) + + if not math.isfinite(xmin) or not math.isfinite(xmax) or xmin == xmax or ymin == ymax: + xmin, xmax, ymin, ymax = -0.5, 0.5, -0.5, 0.5 + + padding_x = max(0.05 * (xmax - xmin), 1e-3) + padding_y = max(0.05 * (ymax - ymin), 1e-3) + xmin -= padding_x + xmax += padding_x + ymin -= padding_y + ymax += padding_y + + fig, ax = plt.subplots(figsize=(6, 6)) + colour_cache: Dict[str, str] = {} + + for primitive in primitives: + category = primitive.category or "unassigned" + colour = colour_cache.get(category) + if colour is None: + colour = _CATEGORY_COLOURS.get(category, _CATEGORY_COLOURS["unassigned"]) + colour_cache[category] = colour + + if primitive.kind == "polyline" and primitive.points: + xs, ys = zip(*primitive.points) + ax.plot(xs, ys, color=colour, linewidth=1.2) + elif primitive.kind == "circle" and primitive.center and primitive.radius is not None: + circle = Circle(primitive.center, primitive.radius, fill=False, linewidth=1.2, color=colour) + ax.add_patch(circle) + + ax.set_aspect("equal", adjustable="box") + ax.set_xlim(xmin, xmax) + ax.set_ylim(ymin, ymax) + ax.axis("off") + + output_path.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(output_path, dpi=180, bbox_inches="tight", pad_inches=0.05) + plt.close(fig) + return output_path + + +def export_scenario_to_dxf(scenario_path: Path, output_dir: Path) -> List[Path]: + """Convert a scenario JSON into a set of DXF artefacts.""" + + from python.visualize_scenario_field import compute_domain_bounds, gather_boundaries, load_scenario + + spec, wires = load_scenario(scenario_path) + output_dir.mkdir(parents=True, exist_ok=True) + + exported: List[Path] = [] + + # Domain boundary + try: + xmin, xmax, ymin, ymax = compute_domain_bounds(spec) + except ValueError as exc: + raise DxfError(str(exc)) from exc + + domain_doc = ezdxf.new("R2010") + msp = domain_doc.modelspace() + msp.add_lwpolyline( + [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax), (xmin, ymin)], + dxfattribs={"layer": "domain"}, + ) + domain_path = output_dir / "domain.dxf" + domain_doc.saveas(domain_path) + exported.append(domain_path) + + # Material regions + boundaries = gather_boundaries(spec) + region_doc = ezdxf.new("R2010") + region_msp = region_doc.modelspace() + for index, (xs, ys, material) in enumerate(boundaries.get("polygons", [])): + if len(xs) < 2: + continue + coords = list(zip(xs, ys)) + if coords[0] != coords[-1]: + coords.append(coords[0]) + layer_name = f"material_{material or index}" + region_msp.add_lwpolyline(coords, dxfattribs={"layer": layer_name}) + if len(region_msp): + regions_path = output_dir / "materials.dxf" + region_doc.saveas(regions_path) + exported.append(regions_path) + + magnet_doc = ezdxf.new("R2010") + magnet_msp = magnet_doc.modelspace() + for idx, (xs, ys) in enumerate(boundaries.get("magnet_polygons", [])): + if len(xs) < 2: + continue + coords = list(zip(xs, ys)) + if coords[0] != coords[-1]: + coords.append(coords[0]) + magnet_msp.add_lwpolyline(coords, dxfattribs={"layer": f"magnet_{idx}"}) + for idx, rect in enumerate(boundaries.get("magnet_rects", [])): + xmin_r, xmax_r, ymin_r, ymax_r = rect + coords = [ + (xmin_r, ymin_r), + (xmax_r, ymin_r), + (xmax_r, ymax_r), + (xmin_r, ymax_r), + (xmin_r, ymin_r), + ] + magnet_msp.add_lwpolyline(coords, dxfattribs={"layer": f"magnet_rect_{idx}"}) + if len(magnet_msp): + magnets_path = output_dir / "magnets.dxf" + magnet_doc.saveas(magnets_path) + exported.append(magnets_path) + + if wires: + wire_doc = ezdxf.new("R2010") + wire_msp = wire_doc.modelspace() + for idx, wire in enumerate(wires): + wire_msp.add_circle( + center=(wire.x, wire.y), + radius=wire.radius, + dxfattribs={"layer": f"wire_{idx}"}, + ) + wires_path = output_dir / "wires.dxf" + wire_doc.saveas(wires_path) + exported.append(wires_path) + + return exported diff --git a/python/gui/render.py b/python/gui/render.py new file mode 100644 index 0000000..71e8474 --- /dev/null +++ b/python/gui/render.py @@ -0,0 +1,142 @@ +"""Helpers for rendering geometry previews and field visualisations.""" + +from __future__ import annotations + +import io +from pathlib import Path +from typing import Dict, List, Optional + +import matplotlib + +matplotlib.use("Agg", force=True) + +import matplotlib.pyplot as plt # noqa: E402 - backend configured above +from matplotlib.patches import Rectangle # noqa: E402 + +from python.visualize_scenario_field import ( # noqa: E402 + Wire, + compute_domain_bounds, + gather_boundaries, + load_field_csv, + load_scenario, + plot_field, + reshape_field, +) + + +DEFAULT_LOG_FLOOR = 1e-7 + + +def render_geometry_preview(scenario_path: Path, output_path: Path) -> Path: + """Render a static geometry preview for the supplied scenario.""" + + spec, wires = load_scenario(scenario_path) + boundaries = {} + try: + boundaries = gather_boundaries(spec) + xmin, xmax, ymin, ymax = compute_domain_bounds(spec) + except ValueError as exc: + raise RuntimeError(str(exc)) from exc + + fig, ax = plt.subplots(figsize=(6.0, 5.0)) + + width = xmax - xmin + height = ymax - ymin + rect = Rectangle((xmin, ymin), width, height, fill=False, linewidth=1.6) + ax.add_patch(rect) + + for xs, ys, _material in boundaries.get("polygons", []): + if len(xs) < 2: + continue + closed_xs = xs + [xs[0]] + closed_ys = ys + [ys[0]] + ax.plot(closed_xs, closed_ys, color="tab:blue", linestyle="-", linewidth=1.0, alpha=0.7) + + for xs, ys in boundaries.get("magnet_polygons", []): + if len(xs) < 2: + continue + closed_xs = xs + [xs[0]] + closed_ys = ys + [ys[0]] + ax.plot(closed_xs, closed_ys, color="tab:red", linestyle=":", linewidth=1.2, alpha=0.9) + + for xmin_m, xmax_m, ymin_m, ymax_m in boundaries.get("magnet_rects", []): + rect_x = [xmin_m, xmax_m, xmax_m, xmin_m, xmin_m] + rect_y = [ymin_m, ymin_m, ymax_m, ymax_m, ymin_m] + ax.plot(rect_x, rect_y, color="tab:red", linestyle=":", linewidth=1.1, alpha=0.9) + + for wire in wires: + color = "tab:red" if wire.current >= 0 else "tab:blue" + circle = plt.Circle((wire.x, wire.y), wire.radius, color=color, fill=False, linewidth=1.3) + ax.add_patch(circle) + + ax.set_aspect("equal", adjustable="box") + ax.set_xlabel("x [m]") + ax.set_ylabel("y [m]") + ax.set_title(spec.get("name", scenario_path.name)) + ax.set_xlim(xmin, xmax) + ax.set_ylim(ymin, ymax) + ax.grid(False) + + output_path.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(output_path, dpi=180, bbox_inches="tight") + plt.close(fig) + return output_path + + +def render_field_map_image( + scenario_path: Path, + field_map_path: Path, + *, + output_path: Optional[Path] = None, + vector_mode: str = "linear", + quiver_skip: int = 4, + color_scale: str = "linear", + draw_boundaries: bool = True, + streamlines: bool = False, + log_floor: float = DEFAULT_LOG_FLOOR, + vector_log_floor: float = DEFAULT_LOG_FLOOR, +) -> bytes | Path: + """Render the field map with the supplied styling options.""" + + spec, wires = load_scenario(scenario_path) + xs, ys, bxs, bys, bmags = load_field_csv(field_map_path) + grid_x, grid_y, (bx, by, bmag) = reshape_field(xs, ys, (bxs, bys, bmags)) + + boundaries: Dict[str, List] = {} + if draw_boundaries: + try: + boundaries = gather_boundaries(spec) + except ValueError: + boundaries = {} + + buffer = io.BytesIO() + plot_field( + grid_x, + grid_y, + bx, + by, + bmag, + wires, + quiver_skip, + buffer, + f"Field map: {scenario_path.name}", + color_scale, + log_floor, + draw_boundaries, + boundaries, + None, + vector_mode, + vector_log_floor, + streamlines, + None, + 0, + show=False, + ) + + data = buffer.getvalue() + if output_path is not None: + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_bytes(data) + return output_path + + return data diff --git a/python/gui/templates/base.html b/python/gui/templates/base.html index 48172a8..cb9e100 100644 --- a/python/gui/templates/base.html +++ b/python/gui/templates/base.html @@ -3,24 +3,159 @@ - mag_sim Flask GUI + mag_sim Studio + {% block head %}{% endblock %} - -